Error Codes
Reference for REST API and MCP error responses.
Error Response Format
REST endpoints return errors as JSON with a top-level error string. Some errors carry more fields beside it, at the same level:
{
"error": "Product not found",
"code": "product_not_found"
}code: a stable value for an error with a known cause. Match oncode, not on the message text. Errors without a known cause have nocode.errorId: on a generic 500. Quote it when you report the problem.details: a list of validation messages, on some 400s.
A few routes add their own fields, such as path on an unknown route (404) and conflictProjectId on a project rename conflict (409).
HTTP Status Codes
| Status | Description | Typical Cause |
|---|---|---|
| 400 | Bad Request | Invalid JSON or parameters, unknown or disallowed model |
| 401 | Unauthorized | Missing or invalid API key, session or OAuth token |
| 402 | Payment Required | Monthly storage quota used up |
| 403 | Forbidden | Signed in, but the action needs another role or OAuth scope |
| 404 | Not Found | The item is not in this workspace, or the route is unknown |
| 409 | Conflict | A name another item already uses |
| 413 | Payload Too Large | More than 20 images in one bulk request, or a body over its limit |
| 422 | Unprocessable Content | The image provider rejected the workspace's API key, or refused the content (content_blocked) |
| 429 | Too Many Requests | The workspace's own per-minute request limit |
| 500 | Internal Server Error | A provider failure, or an unexpected error |
| 502 | Bad Gateway | ChatGPT subscription routes only: the runner failed |
| 503 | Service Unavailable | A feature that is switched off |
The ChatGPT subscription routes can also answer 422 or 429 with ChatGPT's own reason for a failed image.
Authentication Errors
401 Unauthorized
{ "error": "Invalid API key format. Must start with sk_live_ or sk_test_" }Other messages include Authentication required. Provide API key, session, or OAuth token., Invalid API key. Key not found. and Access token has expired.
The MCP endpoint answers a missing or rejected credential with 401 and a WWW-Authenticate header, which points OAuth clients to the sign-in metadata.
403 Forbidden
{
"error": "This action requires workspace owner or manager access",
"code": "insufficient_workspace_role",
"required_roles": ["owner", "manager"]
}An OAuth access token without the scope a route needs gets a different 403 shape, with a WWW-Authenticate: Bearer error="insufficient_scope" header:
{
"error": "insufficient_scope",
"message": "…",
"required_scopes": ["images:edit"]
}Admin routes answer Admin authentication required (401) and Super admin privileges required (403).
Validation Errors
A request that can never succeed as sent answers 400. Do not retry it: change the request.
code | Cause |
|---|---|
invalid_request | An invalid field or JSON body, or an option the chosen model does not support |
unknown_model | model_id is not a registered model |
model_not_allowed | model_id (or the workspace default) is not on this workspace's allowlist |
invalid_reference_assets | A reference_assets entry was rejected |
invalid_project_name, invalid_tag_name | A project or tag name that cannot be used |
{
"error": "Model 'gemini-pro-image' is not allowed for this tenant. Allowed models: gemini-flash-lite-image, gemini-nano-banana-2.1",
"code": "model_not_allowed"
}POST /api/generations/start uses invalid_generation_request in place of invalid_request and invalid_reference_assets.
Field checks describe what failed in the message or in details. The wording varies by route. POST /api/images answers Invalid request payload with a code and details. PATCH /api/products/:id answers Validation error: … with details and no code. Some older checks answer plain text with no code, such as Invalid JSON payload or Invalid limit.
Resource Errors
404 Not Found
{
"error": "Product not found",
"code": "product_not_found"
}Each kind of item has its own code:
- Images and sessions:
image_not_found,session_not_found,nothing_to_continue(a continue-edit in a session with no image yet). - Saved items:
character_not_found,product_not_found,scene_not_found,style_preset_not_found,reference_group_not_found. - Gallery:
project_not_found,tag_not_found. - Jobs:
identity_refresh_job_not_found.
An item in another workspace answers exactly like a missing one. An unknown route answers Unknown API route with no code.
409 Conflict
{
"error": "Product reference \"Widget\" already exists. Use a different name.",
"code": "product_name_taken"
}Creating or renaming an item to a name another item in the workspace uses answers character_name_taken, scene_name_taken, reference_group_name_taken, style_preset_name_taken, product_name_taken or project_name_taken. Reverting a character identity with nothing archived answers nothing_to_revert. Reusing an Idempotency-Key with a different request body, or for a different operation, answers IDEMPOTENCY_CONFLICT.
Rate & Quota Errors
402 Monthly Quota Exceeded
{ "error": "Monthly quota exceeded: 10240.00MB / 10240MB used. Please upgrade your plan or wait until next month." }The workspace has used its monthly storage allowance. Requests that read or delete (GET, HEAD, DELETE) still work, and so does cancelling a run, so you can inspect and remove images. POST /api/images/delete-bulk is a POST, so it is refused too: delete one image at a time with DELETE /api/images/:id.
429 Rate Limit Exceeded
{ "error": "Rate limit exceeded: 60 requests per minute. Try again in 30 seconds." }This is the workspace's own per-minute limit. The Retry-After header gives the seconds to wait. Successful responses carry X-RateLimit-Limit, X-RateLimit-Remaining and X-RateLimit-Reset.
Provider Errors
422 Rejected Provider Key
{
"error": "Your image provider rejected its API key. Check the provider API key in workspace settings, then try again.",
"code": "provider_credentials_rejected"
}The image provider (Gemini or OpenAI) refused the workspace's key. The message is fixed text, never the provider's, because a provider's text can repeat part of the key. Fix the key in workspace settings, then retry. In a batch or bulk edit, the request still succeeds, and the failed item's error carries this text with code: "provider_credentials_rejected" beside it. A failed async job carries the same errorCode.
422 Content Blocked
{
"error": "Image generation failed: Content generation blocked due to safety filters. Reason: IMAGE_SAFETY",
"code": "content_blocked"
}The provider (Gemini or OpenAI) refused the prompt or the references. That is a safety or policy block, or Gemini stopping an image that matched existing content (RECITATION, whose text asks you to rephrase). The text gives the provider's reason. Go Bananas does not retry it or try another key, and the same request is refused again: change the prompt or the references first. Switching to another model only to get past the filter is not a fix.
- The generate, edit, continue-edit, character, multi-character and product routes answer 422.
- An async job (
POST /api/images?async=true,POST /api/generations/start, orPOST /api/edit-imagewithPrefer: respond-async) endsfailed, and its status (GET /api/executions/:id,GET /api/generations/:id) carrieserrorCode: "content_blocked". - A batch or bulk edit still succeeds, and the blocked item carries
code: "content_blocked"beside itserror.
The ChatGPT subscription routes answer ChatGPT's own refusal with a 422 that reads "ChatGPT declined this prompt…" and has no content_blocked code.
500 from Image Generation
The routes that make images (POST /api/images, the edit and continue-edit routes, and character, multi-character and product generation) return the provider's own message with a 500, because it says what to fix. Examples are a missing provider key, a timeout or an outage.
A content block was a 500 here until October 2026. It is now the 422 above, so a client that retries every 5xx no longer resends it.
A provider's billing limit, daily quota or per-minute rate limit is also a 500 with its message, such as Gemini's Rate limit exceeded for … or OpenAI's OpenAI image API key rate limit exceeded: …. It is not a 429.
Server Errors
500 Internal Error
{
"error": "An unexpected error occurred. Please try again.",
"errorId": "5f0c3c1e-8a2b-4b7e-9d61-0c2f6f1a9e47"
}Database, network and other internal failures get this generic message. Their detail stays in the server log under the errorId. Retry with backoff, and quote the errorId if it keeps failing.
MCP Protocol Errors (JSON-RPC 2.0)
MCP endpoints (/mcp, and the legacy /sse) use JSON-RPC 2.0. A failed tool call comes back as a tool result with isError: true. Most texts start with MCP error <code>::
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"content": [
{
"type": "text",
"text": "MCP error -32602: Image not found: 99999. Use get_session_history or search_images to find valid image IDs."
}
],
"isError": true
}
}MCP Error Codes
| Code | Name | Trigger |
|---|---|---|
-32602 | Invalid Params | A caller mistake: invalid arguments, an unknown tool name, an unknown or disallowed model, an ID or name not in this workspace, a name already in use, a rejected reference_assets entry |
-32603 | Internal Error | Everything else, such as a provider failure, a rejected provider key or an internal error |
-32700 | Parse Error | Malformed JSON in the request |
-32600 | Invalid Request | Missing jsonrpc or method fields |
-32601 | Method Not Found | An unknown JSON-RPC method |
-32700, -32600 and -32601 come in the JSON-RPC error object, not in a tool result. So does an unknown tool name on the stateless route that MCP clients on protocol version 2026-07-28 use; elsewhere it is an isError result.
Argument checks read Invalid input parameters: …, or Input validation error: … without the MCP error -32602: prefix when the MCP library itself rejects the arguments. For a missing item, a name already in use or a model error, a -32602 carries the same message as the matching REST error, sometimes with a hint added. Do not retry it: change the arguments.
A rejected reference_assets entry (two image sources in one entry, a reserved role, scope on a role other than character, more images than the model takes, an image this workspace cannot use or one under 512x512) is a -32602. Its text starts Image generation failed: and names the entry (reference_assets[N]) or the reference limit. Fix the request. Over REST it is a 400 invalid_reference_assets. A stored reference that storage fails to read is a server error instead: -32603 over MCP and 500 over REST, and it can be retried.
A content block is a -32602 whose text ends (code: content_blocked). Rephrase the prompt or change the references. Over REST the same block is the 422 content_blocked. batch_generate adds (code: content_blocked) to the blocked item's line, and get_generation_status returns error_code: "content_blocked" for a blocked job.
A -32603 keeps the provider's message, including billing, daily-quota and rate-limit text. A rejected provider key gets the same fixed text as the REST 422. An internal error gets a generic message with an id:
MCP error -32603: An unexpected error occurred. Please try again. (errorId: <uuid>)Two other forms:
- The workspace's rate limit or storage quota refuses a tool call with an
isErrorresult that startsError:, such asError: Rate limit exceeded: …. It has no JSON-RPC code. start_generationputs JSON such as{ "error": { "status": 400, "code": "unknown_model", "message": … } }in itsisErrorresult instead of an MCP error code.statusis the matching HTTP status.
Handling Errors
- Treat non-2xx status codes as failures even if a legacy endpoint includes
success. - Do not retry a 400, 404 or 409, or an MCP
-32602. The same request fails again. - For a 422
provider_credentials_rejected, fix the provider key before retrying. For a 422content_blocked, change the prompt or the references; the same request is refused again. - For a 429, wait for
Retry-After. For a 500, retry with backoff. - For MCP, check
isErroron the tool result as well aserroron the JSON-RPC response.