Error codes
This is the exhaustive lookup table for every error code the platform emits
today. It complements the conceptual Errors guide (the body shape,
how FlightdeckError surfaces failures in the SDK, and the “a failed read is
never an empty result” rule) — read that first if you have not. This page is
the reference: match a code here to learn its typical status, what it means,
and how to fix it.
Branch your code on code, never on message. The code is a stable,
machine-matchable string; the message wording is unstable and may name the
offending field, scope, or resource id.
The error body
Every non-2xx response is JSON with a single nested error object:
{
"error": {
"code": "invalid_body",
"message": "url and a non-empty events array are required",
"correlationId": "1b9d6bcd-bbfd-4b2d-9b5d-ab8dfbbd4bed"
}
}| Field | Presence | Meaning |
|---|---|---|
error.code | always | Stable machine-matchable string. This is what the tables below key on. |
error.message | always | Human-readable explanation. May name a field/scope/id. Do not parse it. |
error.correlationId | optional | A request-correlation id. Present when the platform attached one; absent otherwise. Include it verbatim in any support report so the failure can be traced in the logs. |
The body is always nested under error — there is no top-level code. In the
SDK, read err.body.error.code.
How to read the status column
A single code can carry more than one HTTP status depending on the surface
that raised it (for example invalid_tenant is normally 400, but a few
internal paths surface it as 422 or 500). The tables give the typical
status and note the exceptions. Handle failures on the status class first
(4xx = your request, 5xx = the server), then refine on code.
A 503 is an honest “we could not find out” — a dependency read was
unavailable. It is never a laundered empty result. Retry a 503 with backoff;
never turn it into [], 0, or a 2xx “nothing here”.
Cross-cutting codes
These are raised by nearly every resource. They are listed once here rather than repeated in every section below.
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
invalid_body | 400 | The JSON request body failed validation (missing/typed-wrong field). | Fix the body against the route’s documented shape. The message names the first offending field. |
invalid_json | 400 | The request body was not parseable JSON. | Send well-formed JSON and a correct Content-Type. |
invalid_query | 400 | A query parameter is malformed or out of range (e.g. a bad limit/offset). | Correct the query string. |
invalid_params | 400 | A path/route parameter is malformed. | Correct the path segment. |
invalid_tenant | 400 | The tenant ref in the path is malformed. (A few internal paths surface it as 422 or 500.) | Use a well-formed tenant slug. |
invalid_value | 400 | A supplied value is outside its allowed set. | Send an allowed value; the message names it. |
invalid_status | 400 | A supplied status value is not a recognized status. | Send a valid status. |
missing_idempotency_key | 400 | A money-mutating call arrived without the required Idempotency-Key header. | Send a deterministic Idempotency-Key (see Money invariants). |
unauthorized | 401 | No valid credential — the key/token is missing, malformed, unknown, or revoked. | Send a valid Authorization: Bearer fdk_... (or the route’s token). |
forbidden | 403 | Authenticated, but the credential lacks the route’s required scope. | Issue/use a key carrying the required scope (see Scopes). |
not_found | 404 | No such resource for this tenant. Returned cross-tenant too, so a probe cannot tell “not yours” from “does not exist”. | Verify the id and that it belongs to your tenant. |
already_exists | 409 | The resource already exists (a uniqueness conflict). | Fetch the existing resource instead of re-creating it. |
precondition_failed | 412 | The resource was not in the state the action requires, or changed concurrently. | Re-read current state and retry. |
rate_limited | 429 | A verification or management limiter is spent. | Respect the retry-after header and back off (see Rate limits). |
internal_error | 500 | An unexpected server-side failure. | Retry with backoff; report the correlationId if it persists. |
lookup_unavailable | 503 | A dependency read failed — not an empty result. | Retry with backoff. |
lookup_failed | 500 / 503 | A read failed. 503 when a dependency was unreachable; 500 for an unexpected failure. | Retry with backoff. |
read_unavailable | 503 | A backing read was unavailable. | Retry with backoff. |
db_error | 500 | A database operation failed unexpectedly. | Retry with backoff; report the correlationId. |
Authentication and credentials
Raised by the credential-verification and operator gates (@dscodotco/stores,
@dscodotco/identity) and by scoped-key issuance. See Authentication
and Scopes.
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
unauthorized | 401 | The fdk_ key or token is missing, malformed, unknown, or revoked. | Send a valid credential. |
invalid_token | 401 | The supplied token (session/preview/storefront) is invalid or expired. One operator path surfaces it as 403. | Obtain a fresh token. |
invalid_session | 401 | The session token is invalid or expired. | Re-authenticate. |
signature_verification_failed | 401 | An inbound signed request failed HMAC verification (webhook gateway). | Sign with the correct secret over the exact raw bytes. |
auth_unavailable | 503 | The credential/session check itself could not complete (the auth dependency was unavailable). | Retry with backoff. This is fail-closed — the request was refused, not admitted. |
forbidden | 403 | The credential lacks the route’s required scope. | Use a key carrying the scope; see Scopes. |
scope_escalation | 403 | A key-issuance request asked for a scope the issuing key does not itself hold. | Request only scopes the issuing key already has (issuance is subset-gated). |
not_an_operator | 403 | The route requires an operator credential; the caller is not an operator. | Use an operator credential for operator-only routes. |
operator_restricted | 403 | The action is restricted to operators (or a role the caller lacks). | Perform the action from an operator surface. |
email_unverified | 403 | The action requires a verified email and the caller’s email is unverified. | Verify the email, then retry. |
Rate limiting
See Rate limits for full behavior.
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
rate_limited | 429 | The credential-verification (or a management) limiter is spent. | Read retry-after (seconds) and wait at least that long before retrying. |
velocity_exceeded | 429 | A checkout velocity gate tripped (too many attempts for this order/scope). | Back off; this is a distinct, non-oracle-sensitive 429 the storefront can retry on. |
db_error / select_failed | 503 | The verification limiter’s backing store was unavailable, so the request was refused fail-closed (message: “rate limiter unavailable”) rather than admitted unmetered. The underlying database code is surfaced verbatim. | Retry with backoff. |
Checkout and the money path
Raised by @dscodotco/checkout (placement, refund, cancel, edit, returns) and by the
@dscodotco/payments failure codes it surfaces. Several of these come from
domain Result codes mapped to HTTP status by checkout’s status mappers, so
the same code can mean the same thing across the operator and merchant
routes. All mutating routes require an Idempotency-Key; a retried key replays
the original outcome (see Money invariants).
Placement and payment
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
missing_idempotency_key | 400 | A checkout/refund/cancel/edit call arrived without Idempotency-Key. | Send a deterministic idempotency key. |
invalid_card | 400 | card.ccnumber and card.ccexp are required for this charge and were missing/malformed. | Supply valid card fields. |
invalid_amount | 400 | An amount (e.g. a partial-refund amount_cents) is missing, non-integer, or out of range. | Send a positive integer-cents amount within bounds. |
invalid_subscription_consent | 400 | A subscribe request lacked the required auto-renewal consent. | Capture and pass explicit subscription consent. |
zero_amount_checkout_unsupported | 501 | A zero-total checkout is not supported. | Ensure the order has a chargeable total. |
card_declined | 402 | The card was declined. | Ask the shopper for another card. Do not auto-retry. |
payment_declined | 402 | The charge was declined (payment or fraud floor). | Show a decline; do not auto-retry the same card. |
provider_declined | 402 | The payment provider declined the transaction. | Show a decline; try another instrument. |
payment_outcome_indeterminate | 502 | The provider returned an indeterminate result — the platform cannot confirm capture. | Do not re-charge blindly; reconcile against the order before retrying (the idempotency key protects a safe retry). |
order_tender_record_failed | 500 | The order was charged but recording a tender line failed. | Retry (idempotent); report the correlationId if it persists. |
replay_lost | 500 | An idempotent replay’s original outcome could not be re-read. | Retry with backoff; report the correlationId. |
@dscodotco/payments codes surfaced at checkout
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
intent_not_found | 404 | No payment intent matches. | Verify the intent id. |
intent_not_authorized | 409 | The intent has no provider authorization to capture. | Re-authorize before capture. |
charge_not_found | 404 | No charge matches the supplied charge_id. | Verify the charge id. |
refund_exceeds_charge | 409 | The requested refund is greater than the charge. | Refund at most the captured amount. |
invalid_intent_status | 409 | The intent is not in a state the action allows. | Re-read the intent’s status. |
capture_reservation_conflict | 409 | A concurrent capture conflicted on the reservation. | Retry after backoff. |
nothing_to_refund | 409 | There is nothing left to refund on the charge. | No action — the charge is already fully refunded. |
Order actions (refund / cancel / edit)
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
order_not_found | 404 | No such order for this tenant/shopper. | Verify the order id. |
order_item_not_found | 404 | No such line item on the order. | Verify the item id. |
already_refunded | 409 | The order is already fully refunded. | No action needed. |
order_fulfilled_use_returns | 409 | A shipped/delivered order cannot be cancelled. | Use a return (RMA) instead of cancel. |
order_not_editable | 409 | The order is not in an editable state. | Only edit orders that are still editable. |
coupon_gone | 409 | A coupon applied to the order no longer exists at re-price time. | Re-price without the missing coupon. |
conflict | 409 | A concurrent modification conflicted. | Re-read and retry. |
empty_order | 412 | The edit/reprice would leave the order with no lines. | Keep at least one line. |
invalid_quantity | 400 / 412 | A line quantity is invalid. 400 on edit; 412 in the pricing engine. | Send a valid positive quantity. |
tax_provider_unsupported | 422 | The store’s manifest declares a tax provider the engine cannot compute — the order was refused, not under-charged (fail-closed tax). | Configure a supported tax provider. |
invalid_order_status | 500 | The order carries an unrecognized status (data integrity). | Report the correlationId. |
Returns (RMA)
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
return_not_found | 404 | No such return. | Verify the return id. |
return_line_not_found | 404 | No such line on the return. | Verify the line id. |
already_in_status | 409 | The return/order is already in the requested status. | No action needed. |
invalid_transition | 409 | The requested state change is not legal from the current state. | Re-read current state; follow a legal transition. |
empty_return | 412 | The return has no lines. | Include at least one line. |
return_exceeds_ordered | 412 | A returned quantity exceeds what was ordered. | Return at most the ordered quantity. |
duplicate_return_line | 412 | The same line appears twice in the return. | De-duplicate the lines. |
Gift-card redemption at checkout
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
gift_card_not_found | 404 | No gift card matches that code. | Verify the code. |
gift_card_void | 409 | The gift card is void. | Use a valid card. |
gift_card_empty | 409 | The gift card has no remaining balance. | Use a card with balance. |
nothing_to_redeem | 409 | There is nothing to redeem against this order. | No action needed. |
Commerce and catalog
Raised by @dscodotco/commerce (public catalog, pricing groups, operator transitions).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
product_not_found | 404 | No such product. | Verify the slug/id and that the product is active. |
variant_not_found | 404 | No such variant. | Verify the variant id. |
coupon_not_found | 404 | The coupon code does not exist. | Use a valid coupon code. |
coupon_inactive | 412 | The coupon exists but is not active. | Use an active coupon. |
coupon_exhausted | 412 | The coupon has no redemptions left. | Use a different coupon. |
price_not_found | 404 | No pricing-group price matches. | Verify the group/variant. |
group_not_found | 404 | No such pricing group. | Verify the group id. |
invalid_asset_ref | 400 | An image ref is not asset:<storeKey>/<sha256>.<ext>. | Send a well-formed asset ref (see Media). |
invalid_slug | 400 | A slug is malformed. | Use a URL-safe slug. |
slug_taken | 409 | The slug is already used by another resource. | Choose a different slug. |
invalid_transition | 409 | An operator order transition is illegal from the current state. | Follow a legal transition. |
Media
Raised by @dscodotco/media (upload presign, finalize).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
unsupported_content_type | 400 | The content_type is not on the allowlist, or is valid only for another kind (e.g. a PDF outside coa_document). | Upload an allowed type for the kind. |
invalid_sha256 | 400 | sha256 is not 64 lowercase hex chars. | Send the correct content hash. |
invalid_asset_ref | 400 | The asset ref is malformed. | Use asset:<storeKey>/<sha256>.<ext>. |
presign_unavailable | 503 | The presign step could not complete. | Retry with backoff. |
confirm_unavailable | 503 | The uploaded object could not be confirmed to exist at finalize. | Retry the finalize after re-uploading. |
Domains and provenance
Raised by the self-serve custom-domain flow (@dscodotco/stores, @dscodotco/provenance).
See Custom domains.
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
invalid_hostname | 400 | The hostname is malformed. | Send a valid FQDN. |
invalid_domain | 400 | The domain value is invalid. | Correct the domain. |
no_partnership | 404 | No partnership/claim exists for this host. | Register the domain first. |
not_verifiable | 409 | The claim has no DCV challenge to verify. | Re-request the claim to get a challenge. |
not_verified | 409 | An edge attach was attempted on a domain that is not yet verified. | Verify the domain first. |
dns_unavailable | 503 | The DNS/verification check could not complete. | Retry with backoff. |
Stores and site builder
Raised by @dscodotco/stores (store, manifest, site versions, preview tokens).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
store_not_found | 409 | The store could not be resolved for this action. | Verify the credential’s tenant. |
store_suspended | 409 | The store is suspended. | Contact the operator. |
store_live | 409 | The action is refused because the store is live. | Perform it before going live, or via the correct route. |
invalid_manifest | 400 | The submitted manifest failed validation. | Fix the manifest against the schema (see Site builder). |
invalid_theme | 400 | A theme token/value is invalid. | Use valid theme tokens. |
invalid_version | 400 | A version reference is malformed. | Use a valid version id. |
version_not_found | 404 | No such site version. | Verify the version id. |
no_version | 409 | There is no version to act on (e.g. publish with none). | Save a draft first. |
invalid_preview_token | 404 | The preview token is invalid or expired. | Mint a fresh preview token. |
manifest_missing | 500 | The active manifest could not be read. | Retry; report the correlationId. |
untrusted_sign_in_url | 400 | A supplied sign-in URL is not on the trusted allowlist. | Use a trusted URL. |
Subscriptions
Raised by @dscodotco/subscriptions.
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
invalid_subscription | 400 | The subscription reference/body is invalid. | Verify the subscription id/body. |
invalid_interval | 400 | The billing interval is invalid. | Use a supported interval. |
invalid_interval_count | 400 | The interval count is invalid. | Use a positive integer count. |
invalid_next_bill_at | 400 | The next-bill timestamp is invalid. | Send a valid future ISO-8601 instant. |
Gift cards
Raised by @dscodotco/giftcards (issue/list surface).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
gift_card_not_found | 404 | No gift card matches that code. | Verify the code. |
gift_card_void | 409 | The gift card is void. | Use a valid card. |
gift_card_empty | 409 | The gift card has no remaining balance. | Use a card with balance. |
invalid_amount | 400 | The issue amount is invalid. | Send a positive integer-cents amount. |
gift_card_unavailable | 503 | The gift-card read/write could not complete. | Retry with backoff. |
Store credit (cash)
Raised by @dscodotco/cash (merchant store-credit sub-app).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
invalid_amount | 400 | The grant amount is invalid. | Send a positive integer-cents amount. |
balance_unavailable | 503 | The balance read could not complete. | Retry with backoff. |
ledger_unavailable | 503 | The ledger read could not complete. | Retry with backoff. |
store_credit_unavailable | 503 | The store-credit surface could not complete a read/write. | Retry with backoff. |
Inventory
Raised by @dscodotco/inventory (items, vendors, purchase orders, receive-against-PO)
and @dscodotco/fulfillment allocation.
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
invalid_supplier | 404 | No such supplier/vendor. | Verify the supplier id. |
insufficient_stock | 409 | The requested quantity exceeds available inventory. | Reduce quantity or restock. |
invalid_item_count | 400 | An item count is invalid. | Send a valid positive count. |
invalid_ref | 400 | A reference (e.g. lot/PO ref) is malformed. | Correct the reference. |
Fulfillment
Raised by @dscodotco/fulfillment (queue, allocate/label/ship/deliver).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
not_found | 404 | No such fulfillment row for this tenant. | Verify the id. |
precondition_failed | 412 | The row was not in the state the action requires. | Re-read the queue row and retry. |
Fraud
Raised by @dscodotco/fraud (ban blocklist, checks).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
ban_not_found | 404 | No such ban entry. | Verify the ban id. |
ban_list_unavailable | 503 | The ban-list read could not complete. | Retry with backoff. |
fraud_check_unavailable | 503 | A fraud check could not complete. | Retry with backoff. |
check_unavailable | 503 | A check dependency was unavailable. | Retry with backoff. |
velocity_exceeded | 429 | A velocity gate tripped. | Back off and retry. |
velocity_events_unavailable | 503 | The velocity-events read could not complete. | Retry with backoff. |
Affiliates
Raised by @dscodotco/affiliates (merchant workspace, commission approval).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
member_not_found | 404 | No such affiliate/member. | Verify the affiliate id. |
affiliate_unavailable | 503 | An affiliate read could not complete. | Retry with backoff. |
affiliates_unavailable | 503 | The affiliates surface could not complete a read. | Retry with backoff. |
commission_unavailable | 503 | A commission read/write could not complete. | Retry with backoff. |
commissions_unavailable | 503 | The commissions list could not complete. | Retry with backoff. |
Marketing
Raised by @dscodotco/marketing (campaigns, audiences, segments, steps).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
invalid_audience_query | 400 | The audience predicate failed to compile. | Fix the predicate; the message describes the compile errors. |
invalid_segment_query | 400 / 500 | A segment query is malformed (400) or failed to read (500). | Correct the query. |
invalid_filter_query | 400 | A filter query is malformed. | Correct the filter. |
segment_not_found | 400 | The audience references a segment that does not exist. | Reference an existing segment. |
invalid_step | 400 | A campaign step is invalid. | Fix the step definition. |
invalid_template | 400 | A step names a template outside the closed comms registry. | Use a registered template. |
not_triggerable | 409 | The campaign type cannot be triggered this way (e.g. abandoned_cart enrolls via the cart-idle sweep). | Use the campaign’s own enrollment path. |
invalid_type | 400 | A campaign/entity type value is invalid. | Use a supported type. |
Comms
Raised by @dscodotco/comms (transactional email).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
invalid_recipient | 400 | The recipient is invalid. | Send a valid recipient. |
invalid_email | 400 | The email address is malformed. | Send a valid address. |
invalid_template | 400 | The template is not in the closed registry. | Use a registered template. |
recipient_suppressed | 403 | The recipient is on the suppression list. | Do not send; respect the suppression. |
recipient_opted_out | 403 | The recipient has opted out. | Do not send; respect the opt-out. |
unsubscribe_unconfigured | 503 | The unsubscribe surface is not configured. | Configure unsubscribe before sending. |
send_failed | 502 | The send failed at the provider. | Retry with backoff. |
Persons
Raised by @dscodotco/persons (CDP).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
invalid_person | 400 | The person reference/body is invalid. | Verify the person id/body. |
not_tripped | 409 | No tripped guardrail exists for this tenant. | No action needed. |
resolve_unavailable | 503 | A person-resolve read could not complete. | Retry with backoff. |
intake_unavailable | 503 | The intake read/write could not complete. | Retry with backoff. |
Media assets and documents (provenance / COA)
Raised by the provenance and document surfaces.
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
invalid_entity_type | 400 | The entity type is not recognized. | Use a supported entity type. |
invalid_doc_type | 400 | The document type is not recognized. | Use a supported document type. |
invalid_expires_at | 400 | The expiry timestamp is invalid. | Send a valid future ISO-8601 instant. |
Billing
Raised by @dscodotco/merchant-billing (read-only merchant invoices/statement).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
not_found | 404 | No such invoice/statement for this tenant. | Verify the id. |
read_unavailable | 503 | The billing read could not complete. | Retry with backoff. |
Workflow and webhooks gateway
Raised by @dscodotco/workflow and the inbound gateway (edges/webhooks-gateway).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
unknown_workflow | 404 | No such workflow. | Verify the workflow id. |
run_missing | 500 | A workflow run row could not be read. | Retry; report the correlationId. |
unknown_tenant | 404 | The inbound delivery names a tenant the gateway cannot resolve. | Verify the tenant mapping. |
unknown_provider | 404 | The inbound delivery names an unrecognized provider. | Verify the provider route. |
missing_event_id | 400 | The inbound delivery lacks the provider event id needed to dedupe. | Ensure the provider sends its event id. |
signature_verification_failed | 401 | The inbound signature did not verify. | Sign with the correct secret over the raw bytes. |
publish_failed | 502 | The inbound delivery was stored, but fan-out onto the event bus failed. | Retry with backoff; the provider’s own delivery retry is the recovery path (the delivery was persisted, never lost). |
dedup_unavailable | 503 | The dedup store was unavailable. | The gateway fails closed; the provider will retry. |
deliveries_unavailable | 503 | The delivery ledger read could not complete. | Retry with backoff. |
transaction_unavailable | 503 | A transaction could not begin/commit. | Retry with backoff. |
Analytics and reporting
Raised by @dscodotco/analytics (merchant reporting).
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
invalid_timezone | 400 | The requested timezone is invalid. | Send an IANA timezone. |
invalid_filter_query | 400 | A report filter is malformed. | Correct the filter. |
list_unavailable | 503 | A report read could not complete. | Retry with backoff. |
account_unavailable | 503 | An account read could not complete. | Retry with backoff. |
registry_unavailable | 503 | A registry read could not complete. | Retry with backoff. |
Other unavailability codes
The *_unavailable family always means the same thing: a dependency read
could not complete, and the platform refused to fake a result. All are 503
and all are safe to retry with backoff. Beyond those already listed above, you
may also see: scan_unavailable, confirm_unavailable, resolve_unavailable,
store_credit_unavailable, commission_unavailable. Treat every member of
this family identically.
Content and validation edge cases
| Code | Status | Meaning | How to resolve |
|---|---|---|---|
unsupported_content_type | 400 | The Content-Type is not accepted for this route/kind. | Send an accepted content type. |
invalid_url | 422 | A supplied URL is well-formed but not acceptable here. | Send an acceptable URL. |
invalid_forward_url | 400 | A forward/callback URL is invalid. | Send a valid URL. |
validation_error | 422 | A well-formed request was semantically unprocessable. | Correct the semantics; the message explains. |
Related
- Errors — the body shape and the SDK’s
FlightdeckError. - Scopes — what
forbiddenandscope_escalationare about. - Rate limits — what
rate_limited/429mean. - Money invariants — idempotency keys and the money path.