Source: https://docs.any.org/reference/errors.html # Errors Every error response from the any server — whatever the status code — has the same body, and every code is a dotted identifier you can switch on. Messages are safe to display; `details` carries only the identifiers you asked about, never secrets or internal paths. ## Shape ```json { "error": { "code": "space.not_found", "message": "space spc_xyz not found", "details": { "spaceId": "spc_xyz" } } } ``` | Field | Meaning | | --- | --- | | `code` | machine-readable handle, `
.` | | `message` | human-readable, display-safe | | `details` | optional structured context — field names, ids, counts | ## HTTP status codes | Status | When | | --- | --- | | 400 | validation error on body, path params or query string | | 401 | `auth.required` — the server has no account booted yet; `access.signature_rejected` — the invite service refused this account's signature | | 403 | ownership gates (`auth.not_managed`, `shutdown.not_managed`, `control.forbidden`), author-only data rules (`chat.not_author`), guest and reader writes (`space.read_only`), ACL roles (`acl.forbidden`), foreign topic namespaces | | 404 | target not found (space, object, type, collection, record, version, route) | | 405 | the surface is not available on the tech space (`space.unsupported`) | | 409 | conflict — duplicate, precondition failed, not converged yet, feature disabled | | 410 | the target existed and is gone for good (`object.deleted`, `record.deleted`) | | 413 | history view too large; request body over the 1 MB limit | | 429 | the invite service is throttling this client (`access.rate_limited`) | | 500 | unexpected engine or server failure (`internal`); the stack goes to the server log only | | 501 | route registered for an SDK surface that is not implemented yet | | 502 | the invite service is unreachable or answered unexpectedly (`access.unavailable`) | | 503 | not ready — request cancelled, shutting down, or the embedder is unreachable | Panics are converted to `500 internal` with a generic message. ## CLI exit codes `0` success · `1` user error or 4xx · `2` server error (5xx) · `3` cannot reach the server. See [CLI](https://docs.any.org/reference/cli.md.txt). ## Code namespace ### Request, auth, account | Code | Status | Meaning | | --- | --- | --- | | `request.bad_json` | 400 | body is not valid JSON | | `request.schema` | 400 | JSON shape does not match the endpoint schema | | `request.missing_field` | 400 | a required field is absent — object create without `type`, a bundle body that declares nothing and has no `rootType` | | `request.unknown_field` | 400 | a top-level key outside the accepted set (`details.fields`, `details.accepted`) | | `request.invalid_field` | 400 | a field value the endpoint refuses (bad identity, dataset outside an allowlist, …; `details.field`) | | `request.bad` | 400 | a malformed request the framework or a query parameter check rejected | | `request.not_found` | 404 | no route matches the path | | `request.method_not_allowed` | 405 | the path exists, the method does not | | `request.too_large` | 413 | body over the 1 MB limit | | `auth.required` | 401 | server unauthorized — `POST /v1/auth` first | | `auth.already_authorized` | 409 | `{}` while an account runs — a fresh account is never minted in place | | `auth.not_managed` | 403 | standalone server: a different account on `POST`, or `DELETE` — switching means a restart | | `auth.account_mismatch` | 409 | managed server runs another account; pass `replace: true` to switch | | `auth.bad_mnemonic` | 400 | BIP-39 validation failed | | `auth.mnemonic_mismatch` | 409 | wallet on disk disagrees with the phrase/index | | `auth.account_not_found` | 404 | `accountId` has no local wallet | | `auth.account_in_use` | 409 | another process holds the account's instance lock (`details.pid` when known) | | `auth.passkey_required` | 400 | encrypted wallet; no or wrong passkey in the configured env var | | `auth.device_key_corrupt` | 500 | managed: the account's cached `device.key` is unreadable; never re-minted silently — remove it to mint a new device identity | | `auth.network_mismatch` | 409 | the account's data belongs to a different any-sync network than the server's config (`details.pinned`, `details.configured`) — use one data root per network | | `auth.network_pin_corrupt` | 500 | the account's `network.json` is unreadable; it is never rewritten from config — remove it and start on the account's network | | `control.forbidden` | 403 | managed server: control token (`X-Any-Control-Token`) missing or wrong | | `shutdown.not_managed` | 403 | standalone server refuses `POST /v1/shutdown`; use `any stop` or a signal | | `identity.not_found` | 404 | the identities directory has never seen this identity | | `access.disabled` | 409 | `POST /v1/account/access-code` with no `access.redeemUrl` configured | | `access.request_rejected` | 400 | the invite service refused the request (`details.code`) | | `access.signature_rejected` | 401 | the invite service could not verify this account's signature | | `access.code_not_found` | 404 | unknown invite code | | `access.code_unusable` | 409 | disabled, expired or exhausted code (`details.code`) | | `access.rate_limited` | 429 | the invite service is throttling this client | | `access.unavailable` | 502 | the invite service is unreachable or answered unexpectedly | ### Spaces, members, invites | Code | Status | Meaning | | --- | --- | --- | | `space.not_found` | 404 | unknown space id | | `space.not_accepted` | 409 | join pending approval; space not materialized | | `space.deleted` | 409 | the row is a tombstone; a declined or withdrawn join is re-requestable via `POST /v1/spaces/join` | | `space.join_not_pending` | 409 | `POST …/acl/cancel-join` with nothing to withdraw: the row is not `joining`, or the owner already accepted (the row settles to `active` on its own) | | `space.derived_unknown` | 404 | name outside the derived-spaces registry | | `space.derived_undeletable` | 409 | derived spaces are permanent | | `space.not_invite_pending` | 409 | invite accept on a row not awaiting approval | | `space.already_member` | 409 | a guest token for a space this account already tracks as a member | | `space.read_only` | 403 | a guest or reader tried to write | | `space.unsupported` | 405 | the surface is not available on the tech space (object and type lifecycle, members, files, chat, editor, history, search, metadata, settings, non-bundle writes, catalog setup) | | `members.not_found` | 404 | unknown member identity in this space | | `acl.forbidden` | 403 | the caller's role does not allow this ACL operation | | `acl.record_not_found` | 404 | the ACL record named (a join request) does not exist | | `invite.invalid` | 400 | invite token malformed or unrecognized | | `invite.not_found` | 404 | unknown invite record | | `invite.duplicate` | 409 | an invite already exists for this space | | `guest_key.not_found` | 404 | revoke with no active guest key | ### Objects, bundles, catalog | Code | Status | Meaning | | --- | --- | --- | | `object.not_found` | 404 | object unknown or deleted in this space | | `object.deleted` | 410 | `GET …/objects/:objectId` on a deleted object — distinct from never-existed | | `object.derived_undeletable` | 409 | `DELETE` on a derived object (a bundle root installed with `derived: true`, such as the general chat) | | `object.id_required` | 400 | the object id is a serialized nil (`"None"`, `"null"`, `"undefined"`) | | `record.deleted` | 410 | a write addressed a tombstoned record; the id is burned for good | | `bundle.not_found` | 404 | no live install for the bundle id in this space | | `bundle.not_ready` | 409 | registry not converged, or the winner's tree is not local yet — retry | | `bundle.loser_not_ready` | 409 | losing root not fully synced / grace period not elapsed — retry | | `bundle.not_loser` | 409 | resolve targeted the winner or an unclaimed root | | `bundle.reserved` | 409 | a client ensure with an id under the server's `system:` prefix | | `catalog.not_found` | 404 | no usecase with that id in the catalog (`details.usecaseId`) | ### Datasets, filters, upsert | Code | Status | Meaning | | --- | --- | --- | | `dataset.unknown` | 400 | a record write names a storage collection the space does not serve as a records dataset (a module's own is never upsertable); a read of an unknown dataset answers `200 {"records": []}` | | `dataset.not_declared` | 400 | a write into a storage collection the object's type does not declare — set the declaring type first (collections declare no datasets); also a property value written under an owner the object does not have — set that type or file that collection first | | `dataset.not_found` | 404 | the editor route's `:collection` is not an editor storage collection in this space | | `dataset.validation` | 400 | schema or handler rejected the ops | | `dataset.key_conflict` | 409 | a part or dataset with this key already exists on the type (`details.key`) | | `dataset.shared_conflict` | 400 | `shared` on a module without a canonical storage collection, a shared key that is not the canonical name, or a namespaced dataset on a shared-only module | | `dataset.module_unknown` | 400 | the dataset names a module the server does not compile in | | `dataset.module_owned` | 409 | fields declared on a module-served dataset | | `dataset.module_reserved` | 400 | a part, dataset or bundle draft names a module reserved to the server's own installs (`chat`) | | `dataset.decl_invalid` | 400 | malformed part or dataset declaration | | `dataset.immutable` | 400 | PATCH of a pinned part, definition or field path (`details.path`) | | `upsert.requires_user_ids` | 400 | dataset not declared `idRule: user` | | `upsert.immutable_field` / `upsert.not_author` / `upsert.record_deleted` / `upsert.rejected` | 200 | per-record codes inside `rejections[]` — never HTTP errors | | `filter.unknown_operator` | 400 | operator outside the grammar (`details.operator`, `details.path`) | | `filter.invalid` | 400 | any other filter-grammar violation | | `aggregate.bad_pipeline` | 400 | unparseable pipeline, unknown stage, or `$text`/vector outside the pushdown prefix | | `aggregate.limit_exceeded` | 400 | a blocking-stage bound blew (`details.limit`: `group` | `accumArray` | `memory`) | ### Types, collections and properties | Code | Status | Meaning | | --- | --- | --- | | `type.not_found` | 404 | unknown typeId (400 inside a bundle ensure) | | `type.not_a_type` | 400 | a `…/types` route, or `POST …/properties/:objectId/type/:typeId`, names a user **collection** (`details.collectionId`) — use the `…/collections` routes. A registered collection id (`miniapp`, `bin`) answers `404 type.not_found` | | `type.xkey_required` | 400 | a type or a collection created without an `xKey` | | `type.xkey_conflict` | 409 | `xKey` collides with an existing type's or collection's xKey or id (`details.xKey`, `details.existingTypeId` or `details.existingCollectionId`) — the two surfaces share one handle namespace; also raised by a bundle or catalog install | | `type.registered` | 400 | add/patch/remove a property, part or dataset, or PATCH the type, on a registered built-in type; also a column write on a registered built-in collection | | `type.reserved_carrier` | 400 | object create, `POST …/type/:typeId` or an `any.type` op names a type only its own root may carry (the general-chat root; `details.typeId`) | | `collection.not_found` | 404 | unknown collectionId (400 inside a bundle ensure) | | `collection.not_a_collection` | 400 | a `…/collections` route, or `POST …/properties/:objectId/collections/:collectionId`, names a user **type** (`details.typeId`). A registered type id (`page`, `dataview`) answers `404 collection.not_found` | | `collection.registered` | 400 | PATCH the metadata of a registered built-in collection (`miniapp`, `bin`) | | `membership.wrong_slot` | 400 | a raw `…/modify` write put a known collection id in `any.type` or a known type id in `any.collections` | | `membership.type_required` | 400 | a raw write cleared `any.type`; every object has exactly one type | | `property.not_found` | 400 | a value write names a property the owner — the object's type or one of its collections — does not declare | | `property.kind_mismatch` | 400 | write violated the immutable kind | | `property.xkey_conflict` | 409 | another property of the same type or collection holds this `xKey` (`details.xKey`, `details.existingPropId`) | | `property.immutable` | 400 | PATCH of a pinned path — `kind`, `scope`, `items`, `properties` (`details.path`) | | `property.format_invalid` | 400 | descriptor vocabulary problem on create/PATCH: slug does not fit the kind, reserved slug or key (`tags`, `validate`, `compute`), unparseable `relation.filter` | | `property.format_violation` | 400 | a value violated its descriptor's current slug (`details.propId`, `format` = the slug, `reason`) | ### Chat, editor, history | Code | Status | Meaning | | --- | --- | --- | | `chat.text_required` | 400 | no text, attachments or control | | `chat.text_too_long` | 400 | text over 32 KiB (`details.max_bytes`, `got_bytes`) | | `chat.reply_id_invalid` | 400 | `replyToMessageId` over 256 bytes | | `chat.agent_invalid` | 400 | malformed `agent` group | | `chat.attachments_invalid` | 400 | malformed `attachments` map | | `chat.context_invalid` | 400 | malformed `context` | | `chat.control_invalid` | 400 | malformed `control` | | `chat.emoji_invalid` | 400 | reaction emoji empty or over 64 bytes | | `chat.not_author` | 403 | edit/delete of someone else's message | | `chat.not_found` | 404 | unknown message id | | `blocks.type_required` | 400 | block create without a `type` | | `blocks.not_found` | 404 | unknown block id | | `markdown.no_match` | 400 | `edits[i].oldText` not in the current rendering (`details.editIndex`) | | `markdown.ambiguous_match` | 400 | `oldText` occurs more than once without `replaceAll` (`details.occurrences`) | | `markdown.overlapping_edits` | 400 | two edits matched intersecting text (`details.editIndices`) | | `history.version_not_found` | 404 | unknown version, or not in this object's DAG | | `history.view_too_large` | 413 | narrow with `dataset` / `recordId` | | `history.truncated` | 404 | reserved — the causal past is not on this device | ### Files, devices, push | Code | Status | Meaning | | --- | --- | --- | | `file.not_found` | 404 | unknown fileId/objectId, or files query before the first attach | | `file.not_durable` | 409 | offload refused: local bytes are the only copy | | `file.not_available` | 409 | content not local and not fetchable yet — retry | | `file.variant_invalid` | 400 | variant/variantOf pairing broken | | `device.not_found` | 404 | unknown peer id (or already pruned) | | `device.self_delete` | 400 | refusing to prune this server's own row | | `device.pruned` | 409 | self-row write absorbed by a sticky tombstone | | `push.disabled` | 409 | no push node configured | ### Local store | Code | Status | Meaning | | --- | --- | --- | | `local.disabled` | 409 | `local.enabled: false`; existing collections stay on disk | | `local.collection_not_found` | 404 | local storage collection not ensured yet, or already dropped | | `local.doc_not_found` | 404 | get, or update without `upsert`, of an unknown id | | `local.duplicate_id` | 409 | insert of an id that already exists | | `local.unique_violation` | 409 | a unique index rejected the write | | `local.too_many_docs` | 400 | insert/upsert over 1000 docs in one request (`details.max`, `got`) | | `local.bad_name` | 400 | scope, spaceId or name failed validation | | `local.bad_index` | 400 | an index was rejected (same name, different definition; invalid name), including during import | | `local.bad_export` | 400 | import file has invalid gzip, format/version, document sections or storage names; `details.imported` counts fully completed collections; earlier chunks may remain committed | | `local.bad_filter` / `local.bad_sort` / `local.bad_modifier` | 400 | the filter, sort key or modifier did not parse | | `local.bad_pipeline` | 400 | unparseable pipeline, a sink into the aggregated storage collection, a sink result without `id`, `$lookup` from another one | | `local.bad_sink_target` | 400 | `$out` / `$merge into` / `$lookup from` names a storage collection outside the local store | | `local.limit_exceeded` | 400 | a blocking-stage bound blew (`details.limit`) | ### Events, processes, search | Code | Status | Meaning | | --- | --- | --- | | `events.payload_too_large` | 400 | `data` exceeds 64 KiB | | `events.no_read_key` | 409 | network-scope event in a space without a read key | | `events.too_many_patterns` | 409 | the space's 100-pattern pub/sub budget is exhausted | | `events.topic_not_owned` | 403 | type maps into another account's self-owned topic namespace | | `process.not_found` | 404 | progress/finish/cancel on a process not live under this account | | `process.ambiguous` | 409 | several publishers run the same id (`details.identities`) | | `index.disabled` | 409 | search index turned off (`index.enabled: false`); also the link and backlink reads | | `index.no_embedder` | 400 | `mode: vector` without an embedder | | `index.embedder_unavailable` | 503 | embedder configured but unreachable or over the query budget — retryable | | `search.bad_mode` | 400 | mode not `hybrid` | `fts` | `vector` | | `search.bad_scope` | 400 | scope not a valid slug (`[a-z0-9_-]`, max 64) | ### Engine and server | Code | Status | Meaning | | --- | --- | --- | | `sdk.not_implemented` | 501 | placeholder route (sync-status peers, type delete, collection delete) | | `sdk.not_found` | 404 | the engine reports the target gone (deleted object, unknown property or definition) | | `sdk.crdt_version_newer` | 409 | the account's data was written by a newer release (`details.stored` > `details.supported`): booting it refuses, a running server turns read-only until upgraded (`GET /v1/health` → `crdtVersion`) | | `server.unavailable` | 503 | request cancelled / server shutting down | | `server.internal` | 500 | the embedded usecase catalog failed to compile | | `internal` | 500 | catch-all | > **Note.** `/modify` and `/aggregate` bodies hand their op and stage vocabularies straight to the engine, so unknown top-level keys there are dropped rather than rejected. Every other strict endpoint is listed with `additionalProperties: false` in `GET /v1/openapi.json`. `make catalog-validate` reports catalog problems offline with their own codes — `catalog.bad_yaml`, `unknown_field`, `bad_id`, `duplicate`, `missing`, `bad_field`, `unknown_usecase`, `cycle`, `broken_link`, `bad_miniapp` — one `: : : ` line each; they never appear on the wire.