| 1 |
import { __ } from "@wordpress/i18n"; |
| 2 |
import { decodeEntities } from "@wordpress/html-entities"; |
| 3 |
|
| 4 |
/** |
| 5 |
* Normalize any AI-request rejection into a friendly, human-readable string. |
| 6 |
* |
| 7 |
* The AI Content Suite modals (FAQ AI, Glossary AI, Write/Edit with AI) all hit |
| 8 |
* REST endpoints that, on a provider failure, return a WP_Error. Depending on the |
| 9 |
* transport the rejection carries the message inconsistently — sometimes at |
| 10 |
* `error.message`, sometimes `error.data.message`, sometimes `error.responseJSON.message` |
| 11 |
* — and some reset paths stored a bare object. Reading `${error.message}` blindly |
| 12 |
* printed "undefined", and storing the object printed "[object Object]". |
| 13 |
* |
| 14 |
* This always returns a string, extracts the message in priority order, and maps the |
| 15 |
* common "bad API key" case (the provider text on an OpenAI 401) to an actionable line. |
| 16 |
* |
| 17 |
* @param {*} error The caught rejection (object, Error, string or nullish). |
| 18 |
* @param {string} fallback Optional generic message when nothing usable is found. |
| 19 |
* @return {string} A human-readable, non-empty error string. |
| 20 |
*/ |
| 21 |
export const normalizeAIError = ( error, fallback ) => { |
| 22 |
const generic = |
| 23 |
fallback || __( "Something went wrong. Please try again.", "betterdocs" ); |
| 24 |
|
| 25 |
if ( ! error ) { |
| 26 |
return generic; |
| 27 |
} |
| 28 |
if ( typeof error === "string" ) { |
| 29 |
return error; |
| 30 |
} |
| 31 |
|
| 32 |
const code = |
| 33 |
error.code || ( error.data && error.data.code ) || ""; |
| 34 |
const status = |
| 35 |
( error.data && error.data.status ) != null |
| 36 |
? error.data.status |
| 37 |
: error.status; |
| 38 |
|
| 39 |
const raw = |
| 40 |
( error.data && error.data.message ) || |
| 41 |
error.message || |
| 42 |
( error.responseJSON && error.responseJSON.message ) || |
| 43 |
""; |
| 44 |
const rawText = typeof raw === "string" ? raw : ""; |
| 45 |
|
| 46 |
// The provider wraps an OpenAI 401 as "Incorrect API key provided: ...". Surface a |
| 47 |
// single, actionable line that points at where the key is configured. |
| 48 |
const looksLikeBadKey = |
| 49 |
status === 401 || |
| 50 |
status === 403 || |
| 51 |
code === "invalid_api_key" || |
| 52 |
code === "incorrect_api_key" || |
| 53 |
code === "missing_key" || |
| 54 |
/incorrect api key|invalid api key|unauthor|api key/i.test( rawText ); |
| 55 |
|
| 56 |
if ( looksLikeBadKey ) { |
| 57 |
return __( |
| 58 |
"Incorrect or missing API key. Check your key in Settings → AI Content Suite.", |
| 59 |
"betterdocs" |
| 60 |
); |
| 61 |
} |
| 62 |
|
| 63 |
return rawText ? decodeEntities( rawText ) : generic; |
| 64 |
}; |
| 65 |
|
| 66 |
export default normalizeAIError; |
| 67 |
|