Skip to content

Error code reference

Every error response is a {"errors": [...]} envelope (SPEC-DATA-0006); each entry carries one of the codes below, plus the domain naming which subsystem produced it. This page is generated from the api_types::code registry – it cannot drift from what the server actually emits.

Code HTTP status Description
ABSTRACT_OBJECT 422 A record write targeted an object declared abstract.
AMBIGUOUS_REFERENCE 422 A field reference in a query is ambiguous across joined objects.
COMPOSITE_NOT_SCALAR 422 A bare composite field reference is used where only a scalar is valid.
COMPOSITE_ROOT_PATH_CONFLICT 422 A composite field’s root path conflicts with another selected field or path.
DECODE_ERROR 500 A fetched column could not be decoded as the expected Rust type.
DISCRIMINATOR_PAIR_REQUIRED 422 A polymorphic lookup’s discriminator and id must both be present or both absent.
DUPLICATE_ELEMENT 422 A list-typed value contains a duplicate element where uniqueness is required.
EMPTY_LIST 422 A list-typed value is empty where at least one element is required.
FIELD_KEY_MISMATCH 422 A request body’s field key does not match the URL path’s field key segment.
FIELD_NOT_IN_GROUP_BY 422 A SELECT field is not present in the query’s GROUP BY clause.
FIELD_NOT_UNIQUE 400 An upsert targeted a field not marked as a unique natural key.
FIELD_WITHOUT_GROUP_BY 422 A field is referenced without the GROUP BY clause the query requires.
FILTER_PREDICATE_VIOLATED 422 A row fails a declared filter predicate (e.g. a cart/junction row-level constraint).
FK_NOT_FOUND 422 A lookup value references a record id that does not exist in the target object.
FORBIDDEN 403 The principal is not authorized to perform the requested action.
INCOMPLETE_DATE_RANGE 422 A date-range value is missing its start or end bound.
INCOMPLETE_TIMESTAMP_RANGE 422 A timestamp-range value is missing its start or end bound.
INCONSISTENT_TO_TYPE 422 A polymorphic lookup’s declared type does not match its resolved target’s type.
INTERNAL 500 An unexpected internal error occurred; detail is scrubbed server-side.
INVALID_AGGREGATE 422 A query used an aggregate function outside a valid position or on an invalid type.
INVALID_DATERANGE_SHAPE 422 A date-range JSON value does not match the expected {start, end} shape.
INVALID_DATE_RANGE 422 A date-range value has its start after its end.
INVALID_FIELD_REF 422 A field reference in a query does not resolve to a real field.
INVALID_FORMAT 422 A field value does not match its declared format constraint.
INVALID_HAVING 422 A HAVING clause references a field or expression it may not.
INVALID_ID 400 A record id path parameter does not parse as a UUID.
INVALID_KEY 422 A metadata module/object/field key path parameter failed charset or traversal validation.
INVALID_MONEY_AMOUNT 422 A Money value’s amount is not a valid decimal.
INVALID_MONEY_CURRENCY 422 A Money value’s currency code is not a recognized ISO 4217 code.
INVALID_MONEY_SHAPE 422 A Money JSON value does not match the expected {amount, currency} shape.
INVALID_NARROWING_TARGET 422 A type-narrowing field-type change targets an incompatible type.
INVALID_QUERY 400 A HOQS query failed to parse, validate, or execute.
INVALID_RELATIONSHIP 422 A relationship reference does not resolve to a declared relationship.
INVALID_SCHEMA 422 A query or write references a module/object schema that does not exist.
INVALID_TARGET_TYPE 422 A field-type change’s target type is not a valid narrowing or widening.
INVALID_TIMESTAMPRANGE_SHAPE 422 A timestamp-range JSON value does not match the expected {start, end} shape.
INVALID_TIMESTAMP_RANGE 422 A timestamp-range value has its start after its end.
INVALID_TRANSITION 422 A relationship strategy transition is not supported.
INVALID_TRAVERSAL 422 A query traversal (dot-path) is malformed or crosses an unsupported boundary.
INVALID_TYPE 422 A field value’s JSON type is incompatible with the field’s declared type.
JSONB_PATH_NOT_IN_SCHEMA 422 A JSONB path reference does not resolve against the object’s declared schema.
JSONB_PATH_QUERY_NOT_GROUPABLE 422 A JSONB path query is used in a position that requires a groupable expression.
JSONB_PATH_QUERY_NOT_IN_INCLUDE 422 A JSONB path query is used in a position that requires it be in INCLUDE.
JSONB_PATH_QUERY_NOT_ORDERABLE 422 A JSONB path query is used in a position that requires an orderable expression.
JSONB_ROOT_PATH_CONFLICT 422 A JSONB root path conflicts with another selected field or path.
JSON_SCHEMA_VIOLATION 422 A JSON value fails the field’s declared JSON Schema.
LIMIT_EXCEEDED 422 A query result set or list value exceeds the platform’s configured limit.
MAX_ITEMS_EXCEEDED 422 A list-typed value exceeds the field’s declared maximum item count.
MIXED_BETWEEN_TYPES 422 A BETWEEN clause’s two operands have incompatible types.
MIXED_IN_LIST_TYPES 422 An IN list’s elements have inconsistent types.
NOT_A_LOOKUP 422 A lookup/relationship operation was attempted on a field that is not a lookup.
NULL_COMPARISON 422 A comparison operator was used against a null value where it is not valid.
NULL_ELEMENT 422 A list-typed value contains a null element where nulls are not permitted.
OBJECT_NOT_FOUND 404 The requested module/object combination does not exist in metadata.
OUT_OF_RANGE 422 A numeric or date value falls outside the field’s declared valid range.
POLYMORPHIC_NARROWING_REQUIRED 422 A polymorphic lookup write is missing the type narrowing it requires.
READ_ONLY 422 A write attempted to set a formula/rollup (read-only) field.
RECORD_NOT_FOUND 404 No record with the given id exists (or is not visible to the principal).
REF_MODULE_NOT_READY 503 A Money field referenced a currency the reference module has not loaded yet.
REQUIRED 422 A required field was missing from the record payload.
RULE_FAILED 422 A declared ValidationRule (metadata::ValidationRule) evaluated false or null. Not an intra-doc link: api-types has no dependency on metadata (R1 – this crate stays free of domain-crate deps).
SEED_RECORD_IMMUTABLE 403 A write targeted a seed-declared record outside the seed’s own access tier.
STALE_CACHED_PLAN 503 A cached prepared statement no longer matches the schema it was planned against.
SYSTEM_FIELD_ALIAS_CONFLICT 422 A query alias collides with a system field name.
TYPE_MISMATCH 422 A comparison or bind mismatched the operand’s declared type.
UNIQUE_VIOLATION 409 A PostgreSQL unique-constraint violation (23505) surfaced from a write.
UNKNOWN_FIELD 422 The request referenced a field key not defined on the object.
VALIDATION_ERROR 422 A record write failed field- or object-level validation.
Code HTTP status Description
INVALID_BRANCH_REF 400 A {branchId} path segment failed BranchRef::parse’s validation.
METADATA_FIELD_NOT_FOUND 404 A PUT targeted a field that does not exist on the branch.
METADATA_INVALID_KEY 400 A metadata filename resolved outside the metadata directory.
METADATA_MODULE_NOT_FOUND 404 A PUT targeted a module that does not exist on the branch.
METADATA_MODULE_NOT_LOADED 404 The ontology’s {moduleKey} path segment names a module that is not present in the current metadata snapshot.
METADATA_OBJECT_NOT_FOUND 404 A PUT targeted an object that does not exist on the branch.
METADATA_RESOURCE_EXISTS 409 A POST targeted a resource that already exists on the branch.
METADATA_SCREEN_NOT_FOUND 404 A PUT targeted a screen that does not exist on the branch.
METADATA_SNAPSHOT_UNAVAILABLE 503 The ontology was requested before any apply has ever succeeded.
METADATA_WRITE_CONFLICT 409 A concurrent write reached the target branch first.
Code HTTP status Description
APPLY_BRANCH_NOT_ALLOWED 400 POST /apply was sent a branch field, which only POST /plan and POST /metadata@{branchId}/publish accept.
APPLY_DDL_FAILED 500 A DDL statement failed to execute against PostgreSQL.
APPLY_DIAGNOSTICS 422 One or more metadata diagnostics reached Severity::Error or Blocking.
APPLY_INTERNAL 500 A server-side fault with no author-facing cause.
APPLY_LOAD_FAILED 422 Metadata failed to load or compile, or a plan/apply gate rejected the author’s input.
APPLY_SCREENS_NEED_HFX 422 The metadata carries screen files (screens/<moduleKey>/<screenKey>.hfx), and this server was built without HFX support, so it cannot check or serve them. Plan reports the code and apply refuses the whole source: nothing executes and the published metadata does not change. Each screen file is reported on its own entry, targeting its path. Remove the screens/ directory from the source, or run a server built with HFX support. If the same source also fails for other reasons, this code is returned and the full compile message follows in an APPLY_LOAD_FAILED entry.
APPLY_SURFACE_ACTIONS_NEED_APPROVAL 422 A surface in a module manifest lists an action that needs an install approval: admin.metadata.apply or an admin.iam.* action. Plan and apply refuse it for now, because a module surface holding one would act with the signed-in user’s apply or identity-management authority. Each flagged entry is reported with the surface it belongs to; remove it from the manifest and apply again.
APPLY_SURFACE_NEEDS_GRANTABLE_ACTION 422 A module surface declares no grantable action: its actions is empty, or lists only actions that cannot be granted (record and flux actions). A surface is listed to a person only when they hold one of its grantable actions, so such a surface could never be listed to anyone. Plan and apply refuse it (owner ruling O1, #917); each flagged surface is reported by canonical name. Declare the narrowest grantable action the surface needs, such as runtime.screen.render for a surface that only renders screens (every declared action widens its token’s ceiling), and apply again. The platform’s own surfaces are exempt.
APPLY_UNKNOWN_SURFACE_ACTIONS 422 A surface in a module manifest lists an actions entry that is not a known action. Each unknown entry is reported with the surface it belongs to; correct the manifest and apply again.
APPLY_UNRECOGNISED_TYPE_DRIFT 422 A column’s current type and the type your metadata asks for differ in a way the server cannot explain, so it refused the whole apply and changed nothing. This is almost always a bug in the server’s type comparison, not something you did. First review the two types in the message against your metadata and the live column. If they should match, report it at https://github.com/henceforth-labs/henceforth/issues/1094 with the two types from the message. If one apply has both an unrecognised type drift and an unsafe type narrowing, this code is returned and the message lists both. Escape hatch: applying with destructive: true (--destructive on the CLI) rewrites the column to the type your metadata asks for. The server could not explain the difference, so it cannot tell you what the rewrite does to existing values: data in that column may be converted lossily or lost. The column’s current values are vaulted first unless you also pass vault: false. Use the escape hatch only after confirming the column holds nothing you need.
Code HTTP status Description
BRANCH_NOT_FOUND 404 A named branch was looked up but does not exist.
INTERNAL 500 An error from the underlying libgit2 library. Also: An I/O error during file operations in the worktree.
INVALID_BRANCH_REF 400 A caller-supplied branch name failed charset, length, or path-traversal validation.
MAIN_IS_NOT_A_CART_BRANCH 400 main named where a cart branch was expected.
METADATA_NOT_IN_CHECKOUT 409 The server’s metadata directory is not inside a git working tree.
WORKTREE_DIRTY 409 The metadata working tree has uncommitted edits.
WORKTREE_LOCKED 409 An external process holds a git worktree lock.
Code HTTP status Description
CHALLENGE_EXPIRED 401 The SSH challenge nonce has passed its TTL.
CLAIM_NAME_REQUIRED 400 The signed claim payload’s name was empty.
CODE_EXPIRED 401 The one-time session code (exchange or redeem) has passed its TTL, or the token that minted it has expired.
CONFIRMATION_REQUIRED 409 The session code is live, but this browser has not proven it is already signed in as the code’s principal. Nothing was consumed or issued. The page asks the user, then retries with confirm: true.
CROSS_SITE_REQUEST 403 A state-changing auth request carried Sec-Fetch-Site other than same-origin: a browser sent it on behalf of another site.
EXPIRED_TOKEN 401 The JWT exp claim is in the past.
FORBIDDEN 403 The principal is authenticated but not authorized for the action.
INSTANCE_ALREADY_CLAIMED 409 A claim signature arrived at an instance that is already claimed (the one-way latch is set, including when this very request lost the claim race). The client resolves it by logging in.
INSTANCE_UNCLAIMED 503 The instance is unclaimed and the requested route is not one of the handful served while unclaimed (health, status, the SSH challenge and verify). See server::auth::claim::allowed_while_unclaimed.
INTERNAL 500 An internal error (database, crypto, etc.) occurred; detail is scrubbed.
INVALID_CHALLENGE 401 POST /auth/verify’s challengeId does not resolve to a live challenge.
INVALID_CODE 401 The one-time session code (exchange or redeem) does not resolve to a live code.
INVALID_CREDENTIALS 401 Login credentials (username/password) are incorrect.
INVALID_PRINCIPAL_NAME 400 The signed claim payload’s name failed PG-identifier charset or length rules (server::auth::principal_name::validate_principal_name).
INVALID_REQUEST 400 The request’s query string could not be read: a repeated or missing parameter (returnTo twice, no code). Answered after the per-IP gate and counted as a failed attempt, so it cannot be sent for free.
INVALID_RETURN_TO 400 The session exchange’s or redeem’s returnTo is not the name of a mounted surface.
INVALID_SIGNATURE 401 POST /auth/verify’s signature was refused: it does not verify over the received payload, the algorithm or namespace is not accepted, or the signed payload is malformed or does not answer this challenge (wrong nonce, or a purpose that disagrees with the namespace). The message names which, without distinguishing a wrong key from a wrong message.
INVALID_TOKEN 401 JWT signature verification failed or the token is malformed.
MISSING_TOKEN 401 No Authorization header or Bearer token was present.
ORIGIN_MISMATCH 401 The signed challenge payload names an origin this instance does not accept: not one of its --public-origin values, and not a loopback origin on the bound port from a loopback peer. Answered after the signature verified, so it only ever echoes an origin the key holder signed. See server::public_origin::PublicOrigins::decide.
PASSWORD_REQUIRED 400 POST /auth/set-password’s password field was empty.
PASSWORD_TOO_SHORT 400 POST /auth/set-password’s password field is under the minimum length.
PRINCIPAL_NAME_TAKEN 409 The signed claim payload’s name collides with an existing PG role (on this database or elsewhere on the cluster — roles are cluster-wide).
RATE_LIMITED 429 The per-IP or per-principal auth throttle denied this attempt.
SECRETS_DECRYPTION_FAILED 500 AES-256-GCM decryption of a stored secret failed.
SSH_KEY_ALREADY_ENROLLED 409 A claim could not store the declared key because that key is already enrolled on another principal (a credential left by the retired --dev-cert path, for example): one key belongs to one principal (#1020 R5). Distinct from InstanceAlreadyClaimed: the instance is still unclaimed and a client must not fall back to a login. Names no principal.
SURFACE_SCOPE_EXCEEDED 403 The token was issued to a surface whose declared actions do not include the operation. The message names the surface and the action(s) it would have to declare, and nothing about the user’s own grants. Answered before any record or metadata lookup, so it is the same for a resource that exists and one that does not. Also the answer when a surface’s token tries to mint a credential (a session code, a principal, a password reset or an attached credential): no surface may, whatever it declares.
THROTTLE_UNAVAILABLE 503 The per-IP throttle store could not be reached, so the session exchange or redeem failed closed. Nothing was consumed or issued; retry after the Retry-After interval.
UNKNOWN_KEY 401 POST /auth/verify named a key the server has no use for: a login whose publicKeyFingerprint matches no stored credential, or a claim whose signing key (or the key named inside the signed payload) is not the declared claim key, or when no claim key was declared.
UNKNOWN_PRINCIPAL 401 The token’s sub claim does not resolve to a known principal.
UNKNOWN_SURFACE 401 The token was issued to a surface this server no longer serves: its client_id names no embedded or declared surface. Raised at authentication, before the request’s principal is resolved. It follows an uninstall or a rename of the surface key, never a change of mount path. The client signs in again.
Code HTTP status Description
CREDENTIAL_NOT_FOUND 404 The {id} path parameter does not resolve to a known credential.
PRINCIPAL_NAME_INVALID_FORMAT 422 A principal name failed PG-identifier charset or length rules.
PRINCIPAL_NAME_REQUIRED 422 A principal name was empty or missing from the request body.
PRINCIPAL_NOT_FOUND 404 The {id} path parameter does not resolve to a known principal.
SELF_DELETION_FORBIDDEN 422 A principal attempted to delete their own account.
SSH_KEY_ALREADY_ENROLLED 409 The SSH public key is already enrolled, on this principal or another: one key belongs to at most one principal, once (#1020 R5). Names no principal, so the response cannot be used to find out who holds a key.
UNKNOWN_CREDENTIAL_TYPE 422 A credential-type value is not one this server recognizes.
Code HTTP status Description
HFX_EMIT 422 Valid-per-frontend HFX failed to lower to JavaScript, or raw TSX failed.
HFX_PARSE 422 The HFX frontend rejected the source (lex, parse, validate, resolve).
Code HTTP status Description
MODULE_PACKAGE_INCOMPATIBLE 422 The package targets an API contract version this server does not support, or was built against a newer platform release than this server runs. The message names both values.
MODULE_PACKAGE_INVALID 422 The package is not a well-formed .hfmod: not a gzip-compressed tar, an entry that is not a regular file, an unsafe or colliding path, a file missing from or absent in the descriptor’s files, a digest that does not match, or a descriptor or module manifest that is malformed or disagrees with the other. The message names the entry and the rule.
MODULE_PACKAGE_TOO_LARGE 413 The package exceeds a size cap: one file, the whole package, or the number of files. The message names the cap.
MODULE_SIGNATURE_INVALID 422 A first-party package (vendor henceforth or hf, in any casing) is unsigned, signed by a key outside the platform’s first-party key list, or carries a signature that does not verify; or a third-party package carries a signature that does not verify. The entry’s target names which check failed: missing, unlistedKey, invalid or rejectedAlgorithm.
Code HTTP status Description
API_VERSION_REQUIRED 404 The request path matches a route this server served before the /v1 prefix landed (#941); retry it under the versioned prefix.
APPLY_SETTLING 503 A schema apply overlapped this request: it committed while the request was in flight, or just before it, and the server had not finished publishing the new metadata. The request had no effect and is safe to retry, writes included; it is never answered for a write whose commit outcome is unknown. Retry after the Retry-After interval.
CONFIRMATION_REQUIRED 428 The operation changes the instance and asks for confirmation on a terminal; without a terminal, or with --json, it runs only when --yes is given. Nothing was sent. Answered by hf, never by the server.
FORBIDDEN 403 The principal is not authorized to perform the requested action.
INSTANCE_STANDBY 503 Another server process holds this database’s instance lease and is the active instance; this process is a standby and serves only the liveness and readiness probes. Not answered with Retry-After: the standby serves nothing until the active instance exits, which no short interval predicts. Route traffic to the instance whose GET /ready answers 200.
INSTANCE_STARTING 503 This process holds the instance lease but has not finished starting (bootstrap, startup apply, engine start). Safe to retry after the Retry-After interval; GET /ready turns 200 when startup ends.
INTERNAL 500 An unexpected internal error occurred; detail is scrubbed server-side.
INVALID_QUERY_PARAMETER 400 A query-string parameter failed to parse or did not match the expected shape.
INVALID_REQUEST_BODY 422 A JSON request body failed to parse or did not match the expected shape.
NOT_FOUND 404 The requested resource does not exist.
SERVICE_UNAVAILABLE 503 The server is temporarily unable to handle the request.