Skip to main content

Error Codes

Platform errors are described along two orthogonal dimensions: the business code (what exactly went wrong) and the HTTP status (which class of problem this is). This page enumerates every business code and states how the two convert.

Response envelope

Every endpoint returns the same shape:
code === 0 means success. Anything else is a failure, and data is null.
Detecting failure requires checking both the HTTP status and the code: !response.ok || body.code !== 0.HTTP alone is insufficient — some legacy paths can return 2xx carrying a non-zero code. The code alone is insufficient too — an HTML error page from a proxy or auth layer has no code field at all.When parsing the body, read the text first, then parse. Never write .json().catch(() => null): that swallows the crucial fact that the response was not JSON and leaves you with an empty object and no lead.

HTTP status derivation

A business code is not an HTTP status. The server derives one from the other (deriveHttpStatusFromCode):
The key invariant: a thrown error never returns HTTP 2xx.This rule was bought with a production incident. AppError.statusCode once defaulted to 200, so every two-argument business error returned HTTP 200 + non-zero code. The frontend envelope could only detect failure from the code, and an empty message degraded into the self-contradictory Request failed: 200.There are now two safeguards: the status is derived from the code at construction, and the global error handler re-checks — anything with statusCode < 400 is re-derived. The message also has a non-empty fallback.
Uncaught non-AppError exceptions uniformly return:
The message is deliberately sanitised and never exposes an internal stack. Diagnosis relies on the server-side [unhandled] log entry, which carries the method and path.

All error codes

Authentication and accounts

SMS and captcha

The last two are configuration problems, not user problems: seeing them means the server lacks credentials, and retrying will not help.

Files and storage

FILE_TYPE_MISMATCH and FILE_TYPE_NOT_ALLOWED are different: the former means the extension disagrees with the real MIME type (renaming .exe to .png hits it), the latter means the type is not on the allowlist at all. Concrete limits are in Limits.

Skill marketplace

Tasks

TASK_ALREADY_PAID uses 409 rather than 200 on purpose: “already paid” is a state conflict, not a success. Returning 2xx would make the global handler emit 2xx for a thrown error, violating the invariant above.Clients may treat 409 as an idempotency signal — on a duplicate payment submission it means “the outcome you wanted already holds” — but you should still take the failure branch rather than treating this call as successful.

API keys

Recharge

RECHARGE_AMOUNT_MISMATCH appears in the payment callback path and means the channel reported an amount that disagrees with the order. That is a risk signal: do not auto-retry; investigate manually.

Credit consumption

CONSUME_MODEL_RATE_ZERO means the model has no rate configured for that billing unit (for example, a duration-billed model receiving a token-billed request), not that the balance is low. See Billing Formulas.

Subscriptions and teams

Credit account

Payments

PAY_NOTIFY_VERIFY_FAILED and PAY_NOTIFY_SERVICE_NOT_ALLOWED signal that a security boundary was touched, not an ordinary business failure. Seeing them in your logs means someone sent the callback endpoint a request that failed signature verification or pointed at a non-allowlisted service.

Invoices

The 60-day invoicing window is a hard-coded business rule, not a configurable value.

Gift cards

This group contains the only five-digit domain codes on the platform.
Why five-digit codes exist: redemption fails for seven distinct reasons (missing / invalid / redeemed / expired / frozen / batch exhausted / claim limit). Collapsing all of them onto HTTP 400 would leave clients unable to say anything specific. So the business code preserves the distinction while the HTTP status classifies by prefix.This is the clearest illustration of “business code and HTTP status are two dimensions” — the status answers the category, the code answers the specific. Your redemption page should switch on the five-digit code and show seven different messages rather than one generic “redemption failed.”

Infrastructure

503 is the only unambiguously retryable code. Before retrying other 5xx errors, confirm the cause is not configuration or data.

Errors specific to agent invocation

The /v1/* path carries a few error shapes that do not go through ResponseCode:
trialExhausted must be recognised on its own. Folding it into a generic “operation refused” branch makes users believe their account is broken — in reality the trial turns simply ran out, and the right next step is the purchase page, not support.

Error handling pattern

Streaming endpoints add one more trap: when catching HTTPStatusError on an httpx stream(), you must await exc.response.aread() before touching the body, or StreamNotRead is raised and masks the real error.

Handling advice by status

On 404 versus 403: accessing another user’s resource typically returns 404. A 403 would leak the fact that the ID exists, making resources enumerable. So a 404 does not necessarily mean the ID is wrong — it may simply not be yours.

Endpoint Reference

Every /v1/* endpoint

SSE Events

Streaming event types

Authentication

API keys and auth

Limits

The thresholds behind 429
Verified 2026-08-11. Sources: services/core/src/constants/response-code.ts (all business codes), services/core/src/errors/app-error.ts (deriveHttpStatusFromCode), services/core/src/errors/error-handler.ts (global handler).