Skip to content
GhostmotionGhostmotion / DevelopersOpen app

Errors and retries

Public HTTP handler failures use a non-2xx status and a JSON error object. MCP service failures set isError: true and include the same error in structuredContent.error. Input validation failures can instead return text in content without structured content. An authentication or protocol failure may arrive before a tool result.

Example HTTP error
{
  "error": {
    "code": "project_busy",
    "message": "The creator is still working on this project. Wait for the current run to finish, or stop it first."
  }
}

Retry one operation without starting twice

For create_video and send_message, reuse the same request_id and unchanged input after a timeout or transient failure. Save the ID before sending the request. Choose a new ID for a new video or edit. Request IDs belong to the person using the key; changing keys does not make the ID new.

400 invalid_request

Check required fields, supported values, file type and size. Correct the request before retrying.

400 cap_too_low

Increase the run's spending cap within the key's limit so the run can be admitted.

401 unauthorized

Send a valid bearer key. Check whether it expired, was revoked, or its owner left the workspace.

402 insufficient_credits

The workspace needs more credits. With an API key, an owner or admin can use the billing_url in details. An assistant connected by signing in gets no billing link.

402 allowance_exhausted

This key or connection has insufficient remaining allowance, including reservations for active runs. Wait for settlement, or use a key or connection with an appropriate allowance.

403 permission_denied

Check the key's scopes, workspace role and project authorship. Signed upload links must also be valid and unexpired.

404 not_found

Check the identifier and workspace. Inaccessible resources are also reported as not found.

409 project_busy

Wait for the project's current run to finish, or stop it before sending another message.

409 character_not_ready

A cast character has no version yet, or can't be cast. Wait for its run to finish (get_run, character.ready), or check list_characters.

409 idempotency_conflict

This request ID was used with different input. Reuse the original input for a retry, or choose a new ID for a new operation.

409 request_in_progress

The request is still being admitted. Retry the same ID and unchanged input after a short delay.

408 upload_too_slow

The upload exceeded its transfer deadline. Try again; partial files are removed. This legacy code does not imply a minimum upload speed.

409 upload_not_ready

Finish the upload before referencing it. If expired or consumed by another request, prepare and upload a new file.

429 rate_limited

Wait for Retry-After seconds before retrying. REST and MCP share credential limits. Upload admission uses the same code with details.reason=upload_capacity; that refusal has not saved the file.

500 internal_error

Retry with the same request ID and unchanged input after a delay.

507 storage_unavailable

Free server storage or contact the operator, then retry the upload. Incomplete files are removed.

503 temporarily_unavailable

The service is being updated. Retry later with the same request ID and input.

Failures before a handler runs

During a deployment, a 503 can instead contain code: "GM_DRAINING" and an error string. Network proxies can also return non-JSON responses. Check the HTTP status before relying on a response shape, honor Retry-After when present, and retry creation or edits with the same request ID.

A run can fail after being accepted

A successful creation response only means the run was accepted. Poll the run. A later failure may include error_code: "creator_failed" and a readable error. Open project_url to inspect the saved project and continue. Do not assume a failed run was free.

Rate limits

The default limits are 300 requests per IP per minute and 120 per key per minute, shared across REST and MCP on each server instance. Uploads also have concurrency limits. Honor Retry-After on a 429 response and poll_after_seconds when following a run. Avoid rapid retry loops.