Error handling
Two things happen when a generation or edit fails. The editor handles the user: it shows a localized message in place and stays usable, with no wiring needed on your side. Your app gets the uc:error event for everything the built-in UI can't know about: logging, metrics, or reacting to specific failures.
const editor = document.querySelector('uc-ai-image-editor');
editor.addEventListener('uc:error', (e) => {
const error = e.detail.error; // always an AiImageEditorError
console.warn(`generation failed: ${error.code}`, error);
});The event bubbles and crosses shadow DOM boundaries (composed), so a listener on a container, or on document, works too.
The error object
detail.error is always an AiImageEditorError. Whatever actually went wrong (a platform rejection, a network failure, even a custom provider throwing a string) is normalized into it:
| Field | Type | What it tells you |
|---|---|---|
code | AiImageEditorErrorCode | What failed: one of the known codes, or any other string (backend-minted, or frontend-originated like the React wrapper's engine_load_failed). 'unknown' when the failure carried no code (e.g. a network error). |
message | string | The raw, untranslated failure text, for logs rather than for users (show localized messages instead). |
source | string | undefined | Which stage of a job reported the failure, when the backend says (e.g. 'ai_gateway'). Values are backend-defined, so treat them as log enrichment rather than something to branch on. |
cause | unknown | The original thrown value, untouched: the underlying Error, or whatever a custom provider threw. |
Error codes
code is the field to branch on:
import type { AiImageEditorError } from '@uploadcare/ai-image-editor';
function report(error: AiImageEditorError) {
switch (error.code) {
case 'content_moderated':
// the prompt was blocked — nothing to retry
break;
case 'provider_unavailable':
case 'generation_timeout':
// transient — worth offering a retry
break;
default:
console.error('ai-image-editor failed:', error.code, error.message);
}
}Every failure is also logged to the console ([uc-ai-image-editor], with the code, the raw message and the error object), so a code you haven't wired up still shows up while debugging.
The known codes come in four families. Platform validation covers bad input or setup (invalid_source, canvas_too_large, derivative_disabled, and so on). AI gateway covers the generation itself (content_moderated, provider_unavailable, generation_timeout). Auth token covers signed uploads: the Upload API rejecting the token (AccessTokenExpiredError, ScopeForbiddenError, OperationsLimitExceededError, AccessTokenInvalidError, SignatureRequiredError), plus auth_token_failed when your own authToken function throws and no request is made at all. Upload pipeline covers persisting the result (DownloadFileHTTPClientError and friends; yes, PascalCase, because the backend passes those through as literal upload-service class names).
Your handler sees all of them. The editor's own UI is blunter: a code gets its own wording only where that wording changes what the person at the screen does next, and the rest read "Something went wrong. Try again." An expired token and a forbidden scope also read alike, being the same dead end to everyone but you. The localization guide lists every code and the message it shows.
AiImageEditorErrorCode is deliberately open: the known codes are typed as literals (you get autocomplete for them), but the backend can introduce new ones at any time, so any other string flows through rather than breaking. Don't treat the union as closed; keep a default branch.
Enabling AI Image Editor
derivative_disabled means AI generation isn't enabled for your project. The editor won't say that on screen, since a visitor can't act on it, so watch for the code on uc:error or in the console. During the public beta, AI Image Editor is available on all paid Uploadcare plans. Upgrade your plan if you're on a free one, or contact support if you hit this code on a paid account.
React
The React wrapper delivers the same object to onError:
<AiImageEditor
pubkey="YOUR_PUBLIC_KEY"
onError={(error) => console.warn(error.code, error.cause)}
/>One React-specific case: if the lazily-loaded editor engine itself fails to load (e.g. a chunk request dies on a flaky connection), onError receives an AiImageEditorError with code: 'engine_load_failed' and the fallback stays rendered. That code is frontend-originated, so it never appears in the backend families above.
Customizing the in-editor messages
The message the editor shows for a code is a locale string, overridable per-code without touching your event handling:
editor.localeDefinitionOverride = {
en: { 'ai-image-editor-error-content_moderated': 'That prompt isn’t allowed here.' },
};Each code has its own key, so overriding one changes that code alone — several share the same default text, and changing one of those leaves the others as they were. A code with no key (one this version has never heard of) falls back to the generic ai-image-editor-error. See Localization for the mechanics and the full key list.
Server-side code
instanceof AiImageEditorError works in server code too. Import the class from the side-effect-free errors entry, which is safe where the main entry is not (the main entry registers custom elements):
import { AiImageEditorError } from '@uploadcare/ai-image-editor/errors';
if (err instanceof AiImageEditorError && err.code === 'derivative_disabled') {
// e.g. surface a setup hint in your server logs
}What doesn't fire uc:error
- Cancellations. Closing the editor fires
uc:cancel; superseding or aborting an in-flight generation (Start over, unmount) is swallowed silently, since an abort is a user action rather than a failure. - Image display failures. If a result generated fine but its image fails to load into the canvas, that surfaces as a separate
uc:image-errorevent (detail.urlnames the failing URL). The generation itself didn't fail.