Error codes

The grammar

Errors is why a payload carries a code rather than a sentence, English fallback is what a client renders when it has no words for one, and exit codes is the same contract at the CLI.

domain/reason

The domain is the capability’s own name — the same segment that prefixes its tables and namespaces its migrations, which is what makes one capability’s codes incapable of colliding with another’s.

The code is the discriminator, and the taxonomy is closed. The kit’s codes are a union, each status is pinned to the one it maps to, and a mismatched pair fails validation rather than shipping.

Four fields, two of which cross the wire

FieldReaches a clientWhat it is
codeYesStable, and the thing to branch on
messageYesThe problem, in English, permanently
paramsYesStructured values a translating client interpolates
actionNoThe operator’s remedy. Stripped by the HTTP codec
detailNoInternal context for logs and audit. Stripped

The code is already the translation key, so a locale file for the kit’s errors is exactly a file that covers these codes — and there is no second identifier to keep in sync.

What a status means to a client

StatusDo
400Fix the request. Retrying identically will not help
401Mint a fresh access token. If that fails, sign in again
403Do not retry. The caller is who they say and may not do this
404It is not there
409A conflict — usually a state that moved under you
410Gone, permanently
413Too large
429Back off
5xxThe deployment is at fault. Retry with backoff, and page somebody

401 and 403 are the pair worth separating. A client that treats them the same loops: it refreshes a perfectly good token, gets another 403, and refreshes again.

Core, and the cross-cutting domains

CodeMeaning
validation/invalid_inputInput failed validation at a boundary. Carries the failing issues, so a form can highlight the field
core/not_foundThe requested resource does not exist
core/conflictThe state moved under you
core/internalA fault in the deployment
core/upstream_failed, core/upstream_timeoutA third party did not answer, or answered wrongly
core/webhook_unverifiedThe sender’s proof did not check out
core/workflow_failed, core/unknown_workflow, core/invalid_workflow_params, core/missing_workflow_bindingDurable-job faults. The last one is the first-run surprise — a Workflow binding to a host nothing has provisioned
cloudflare/request_failed, cloudflare/invalid_response, cloudflare/not_configuredOutbound, from the CLI or CI. Never from a request handler

Auth

CodeStatusMeaning
auth/invalid_token401The credential is missing, expired or invalid
auth/forbidden403Authenticated, and not allowed to do this
auth/provider_unavailable503An enabled social provider’s credential would not resolve. Every other sign-in method still works

That last one is deliberately not a 404: a provider nobody enabled answers a 404, and nobody enabled this and this is broken right now are different facts that must never share an answer.

Payments

payments/invalid_receipt · payments/verification_failed · payments/webhook_unverified · payments/environment_mismatch · payments/receipt_already_owned · payments/product_not_found · payments/rail_not_configured · payments/provider_unavailable · payments/subject_unresolved · payments/entitlement_required · payments/entitlement_not_in_catalog · payments/discount_invalid · payments/clawback_failed

payments/environment_mismatch is the sandbox guard: a purchase whose store environment does not match this deployment’s is refused outright.

payments/entitlement_not_in_catalog is what a grant of an undeclared key gets — because a grant nothing declared is a grant nobody reviewed.

Storage

storage/not_found · storage/forbidden · storage/quota_exceeded · storage/upload_incomplete · storage/multipart_failed · storage/share_expired · storage/share_revoked

An object you may not see answers not-found rather than forbidden, because a 403 confirms the object exists.

Email

email/template_not_found · email/invalid_payload · email/invalid_token · email/rate_limited · email/send_failed · email/suppressed

email/suppressed is not a failure to fix. The address is on the opt-out list, and that is the list working.

Secrets

secrets/not_found · secrets/already_exists · secrets/invalid_value · secrets/crypto_failed · secrets/rotation_unsupported · secrets/rotation_unrecorded

secrets/crypto_failed is what a name mismatch looks like — a ciphertext moved between rows does not open, which turns a query bug into a clean failure rather than a disclosure.

secrets/rotation_unrecorded exits 3, and it is the one failure the command cannot undo.

Control plane

controlplane/not_connected · controlplane/invalid_credential · controlplane/insufficient_scope · controlplane/key_not_found · controlplane/key_conflict

controlplane/not_connected is the shipped, denying state rather than a fault.

Turnstile

turnstile/missing_token · turnstile/failed · turnstile/config

A wrong secret is turnstile/config, a 500 — not a failed challenge. A 403 would send whoever is debugging it to look at the user, which is the wrong person and an hour gone.

Vector

vector/index_not_found · vector/unfilterable_field · vector/metadata_index_drift · vector/dimension_mismatch · vector/metadata_too_large · vector/filter_too_large · vector/topk_exceeded

vector/metadata_index_drift is the boot refusal — the config declares a filterable field the provisioning record does not have.

Ledger

ledger/insufficient_funds · ledger/invalid_amount · ledger/account_not_found · ledger/currency_not_found · ledger/hold_not_found · ledger/hold_not_open

ledger/insufficient_funds comes from a database constraint, not from a check in a handler — so it holds under concurrency.

Games

Multiplayer: multiplayer/session_not_found · multiplayer/game_not_found · multiplayer/not_a_member · multiplayer/session_full · multiplayer/invalid_move · multiplayer/invalid_transition

Matchmaking: matchmaking/room_not_found · matchmaking/room_full · matchmaking/invalid_code · matchmaking/invite_not_found · matchmaking/invite_forbidden · matchmaking/already_friends · matchmaking/friend_request_not_found · matchmaking/user_not_found · matchmaking/not_queued

Rating: rating/game_not_found · rating/pool_not_found · rating/record_forbidden · rating/unknown_algorithm · rating/unsupported_player_count · rating/invalid_params

Leaderboard: leaderboard/board_not_found · leaderboard/entry_not_found · leaderboard/board_immutable · leaderboard/invalid_schedule · leaderboard/score_rejected · leaderboard/submit_forbidden

rating/unsupported_player_count fails on deploy rather than at the first result — a 1v1 algorithm wired to a four-player game is caught before it produces a month of nonsense.

Media, support, testers, audit

Media: media/not_found · media/unsupported · media/storage_failed · media/enrichment_failed

Support: support/not_found · support/invalid_category · support/rejected · support/unparseable_message · support/classification_failed · support/reply_failed

Testers: testers/not_configured · testers/cohort_not_found · testers/cohort_closed · testers/member_not_found · testers/already_on_roster · testers/withdrawn · testers/roster_full · testers/invalid_token · testers/nudge_cooldown · testers/copy_not_allowed

Audit: audit/invalid_event · audit/write_failed

audit/write_failed never fails the audited action. It is logged and the action proceeds — an audit trail that can take your login down is worse than one with a gap in it.

Where this list comes from

The closed union in @pithy-sh/core’s error payload, plus each capability’s own error module. Those are the authority; this page is a hand-maintained rendering of them.

ESC