Error codes
canonical: spec/v1/artifacts/registry/error-code-registry.json
· catalog version 2026-10-09.4
| code | kind | HTTP | scope / applies to | description |
|---|---|---|---|---|
aad_digest_mismatch | code | 422 | both | Encrypted envelope AAD digest does not match the canonical normalized AAD. |
abuse_network | reason_code | — | moderation_decision | Cross-Realm abuse network signal aggregated by moderation server. |
abuse_review | reason_code | — | moderation_decision | Awaiting moderator review. |
account_binding_principal_mismatch | reason_code | — | identity_creation, client_validation | The authenticated service account is already bound to a principal_id different from the DID locally derived from the frozen identity-creation draft. The client MUST fail closed, visibly disclose the conflict, and MUST NOT adopt the returned principal or silently regenerate a replacement identity. |
account_deactivated | code | 401 | both | The principal account is deactivated. Existing session grants are invalidated and protected requests MUST fail closed; new session issuance MAY surface the same code with a 403 policy-denial status. |
account_erased | code | 401 | both | The principal account is in terminal erasure_pending state. Existing session grants are invalidated and protected requests MUST fail closed with this code instead of a generic unauthenticated response. |
account_locked | code | 401 | both | The principal account is locked. Existing session grants are invalidated and protected requests MUST fail closed; new session issuance MAY surface the same code with a 403 policy-denial status. |
account_status_binding_rollback | reason_code | — | account_status, service_call, state_resolution | A Station received an otherwise valid AccountStatusRecord whose binding_version is lower than the durable floor for the same Account Authority and account. The receiver MUST return failed_precondition with this reason, perform zero replica/outbox/erasure-intent writes, and MUST NOT retry the same record. See zh/identity/account-lifecycle.md §3. |
account_status_record_fork | reason_code | — | account_status, service_call, state_resolution | A Station received an AccountStatusRecord that conflicts with the issuer-ledger chain: either the durable status_seq already names a different record, or the next status_seq does not name the durable head as previous_account_status_record_id. The receiver MUST return failed_precondition with this reason, perform zero writes, stop automatic retry or gap recovery for the conflicting record, and quarantine/alert for operator investigation. It MUST NOT reuse duplicate_conflict, which is reserved by this operation for Idempotency-Key reuse with different canonical request bytes. See zh/identity/account-lifecycle.md §3. |
account_status_record_stale | reason_code | — | account_status, service_call, state_resolution | A Station received an otherwise valid AccountStatusRecord whose status_seq is lower than its durable replica head. The durable head is the only comparison baseline, so this reason applies even when the submitted record is byte-identical to a history row the receiver still stores for that status_seq; a retained history row MUST NOT downgrade the outcome to duplicate. The receiver MUST return failed_precondition with this reason, perform zero writes, and treat the exact record as terminal/non-retryable. duplicate is reserved for a submission whose status_seq and record identity both equal the durable head. The full ordered classification is registry/account-status-replica-decision-table.json. See zh/identity/account-lifecycle.md §3.1. |
account_status_transition_invalid | reason_code | — | account_status, event_envelope, state_resolution | An Account Authority issuer-ledger mutation requested a `from → to` status transition not permitted by the account-status legal-transition table — e.g. reactivating a `deactivated` account. The current-head transaction MUST fail with zero account-row/ledger/audit/outbox writes. A successor after `erasure_pending` uses the more specific `erasure_pending_is_terminal`. See zh/identity/account-lifecycle.md §3. |
account_suspended | code | 403 | both | The principal account is suspended. Existing sessions may observe account state where permitted, but new session issuance and writes MUST fail closed with this code. |
accountability_grant_missing | reason_code | — | event_envelope, auth_decision, service_call | Returned in three surfaces. (1) `event_envelope` / `auth_decision`: an Actor Profile update declares an `accountable_principal_ids[]` entry without a corresponding active `ak.identity.accountability_grant` (issuer=that DID, subject=profile.principal_id, grant_status=active, within validity window). Reducer MUST reject the entire Event with this reason and MUST NOT accept a field-stripped projection. See zh/models/actor.md §3.3.1. (2) `service_call`: returned by orchestrator HTTP operations that fan-out an accountability grant — `ak.self.agent.command.provision.v1` rejects when the controller cannot present an issuable accountability grant for the new agent principal, and `ak.self.agent.command.resume.v1` rejects when the controller's accountability grant over the agent has been revoked or has lapsed its freshness window since `ak.self.agent.command.pause.v1`. HTTP callers MUST treat this as a precondition-class failure, not transient. |
actor_signature_revoked | reason_code | — | event_envelope, auth_decision | An event was signed with a device key that the deactivation/lock fanout has marked revoked. Subsequent ak.self.events.command.submit.v1 signed by that device MUST be rejected. See zh/identity/account-lifecycle.md §7.1. |
aead_nonce_counter_replay | reason_code | — | event_envelope, encoding | An AEAD-encrypted payload (encrypted_payload, blob attachment, to_device, etc.) repeated a per-(key_ref, epoch, device_id) counter already seen by the receiver. The receiver MUST reject the payload before AEAD decryption to prevent attempted nonce reuse against a valid sender device. See zh/crypto-media/media-and-blob.md §3.1 and zh/conformance/encoding.md §10.1. |
aead_nonce_derivation_invalid | reason_code | — | event_envelope, encoding | An AEAD nonce on the wire does not match the deterministic derivation from MLS-Exporter context {key_ref, epoch, purpose} plus (device_id, monotonic counter) mandated by zh/crypto-media/media-and-blob.md §3.1. Producers MUST NOT emit naive random nonces under shared MLS application keys; receivers MUST reject such payloads to enforce the cross-implementation nonce-uniqueness contract. |
agent_authorization_conflicted | code | 409 | both | The accepted Agent key component contains conflicting active authorization state at the target checkpoint. The Event and signer evidence MUST be quarantined. |
agent_authorization_inactive | code | 422 | both | The target Event accepted-at lies outside the Agent key authorization validity interval because the authorization is revoked, superseded, or expired. |
agent_deactivated | reason_code | — | auth_decision, service_call, event_envelope | A request targeted an Agent principal whose current `agent_status` typed current result is `deactivated` (terminal). The endpoint MUST fail closed and no resume path exists. The accepted parent lifecycle witness is sufficient to make all subordinate authorization ineffective; asynchronous cleanup need not synthesize key/grant revoke Events and cannot restore authority. Callers MUST NOT treat this as transient. See zh/identity/key-management.md §3.6.1. |
agent_grant_exceeds_requested_scope | reason_code | — | event_envelope, auth_decision, service_call | A Agent Realm grant contains an action or resource not covered by the Agent's immutable provision requested_scope, or attempts to omit or relax a mandatory provision constraint. Grant attach and reducer admission MUST fail closed with failed_precondition; Realm membership, policy, participation, pairing or key authorization cannot restore authority omitted at provision time. See zh/authz/capabilities.md section 9.1 and zh/identity/key-management.md section 3.6.1. |
agent_key_authorization_expired | reason_code | — | auth_decision | Agent session issuance rejected because the referenced ak.agent.key.authorize declared an expires_at that has elapsed. Distinct from proof_invalid (malformed / unverifiable proof): the runtime should prompt the controller to re-authorize the same key (same-key re-authorization, zh/identity/key-management.md §3.6) rather than rebuild the proof. Never returned for non-expiring (absent expires_at) authorizations. |
agent_key_scope_reauthorization_required | reason_code | — | auth_decision, service_call | The immutable provision ceiling contains every operation required by the selected runtime capability, but the accepted Agent key authorization omits one or more of them. The operation fails with failed_precondition; the controller may re-authorize or rotate the key only within the immutable provision ceiling. See zh/identity/key-management.md §3.6.1 and agent-runtime-scope-registry.json. |
agent_mls_leaf_binding_mismatch | code | 422 | both | An ordinary encrypted Agent Event does not have exactly one historical active BasicCredential leaf whose identity, signature key, and admission authorization lineage match its Agent signer evidence. |
agent_participation_ceiling_unresolved | reason_code | — | event_envelope, auth_decision, service_call | An Agent action or mention fanout requires the current target-local deployment/Realm/Circle/Strand participation policy, but one or more required layers are stale, unavailable, or unverifiable. The action-time gate treats the unresolved policy as all false. The controller's private selection remains stored unchanged. See zh/authz/capabilities.md §5.4. |
agent_participation_ceiling_widen | reason_code | — | state_resolution | Sub-reason for failed_precondition when a Realm/Circle/Strand agent_participation ceiling write would widen (enable a bit disabled by) its parent ceiling. The deployment ⊇ Realm ⊇ Circle ⊇ Strand ceiling chain is tighten-only (monotone). See zh/models/realm-and-space.md, zh/models/circle.md, zh/authz/capabilities.md §5.4. |
agent_paused | reason_code | — | auth_decision, service_call, event_envelope | A request targeted an agent principal whose current `agent_status` typed current result is `paused`. Auth Server MUST reject new agent session grants, and any submit / sidecar / grant-management call by or for the paused agent MUST fail closed until `ak.self.agent.command.resume.v1` lands. Distinct from `capability_denied` so callers can surface the recoverable lifecycle state. See zh/identity/key-management.md §3.6.1. |
agent_pcr_genesis_declaration_conflict | reason_code | — | service_call, event_envelope | A second ak.agent.provision declared a principal_control_realm_id that another accepted provision already claims. Inside one controller PCR the commit-ordered projection claim typed current result rejects it; across controllers the Station's local uniqueness index rejects it. Either way the write set is empty and the earlier claim is untouched. See zh/identity/key-management.md §3.6.3. |
agent_pcr_genesis_declaration_missing | reason_code | — | pcr_genesis, event_envelope | A agent_control ak.realm.create was submitted without an already accepted ak.agent.provision in the controller PCR whose payload.principal_control_realm_id equals retype(this genesis event_id). The genesis carries no ref to its provision, so this reverse look-up is the whole binding: no match MUST fail closed with zero writes, and the receiver MUST NOT materialize the Realm, the agent-status transition or any partial projection. See zh/identity/key-management.md §3.6.3. |
agent_provision_fanout_unavailable | code | 501 | endpoint | Agent provisioning fanout is not available on this deployment. |
agent_provision_scope_migration_required | reason_code | — | auth_decision, service_call | The Agent's immutable provision requested_scope omits an operation mandatory for the selected runtime capability. The operation fails with failed_precondition and recovery requires provisioning a new Agent principal; key re-pairing, Realm grants and session issuance MUST NOT widen this ceiling. See zh/identity/key-management.md §3.6.1 and agent-runtime-scope-registry.json. |
agent_provisioning_already_declared | reason_code | — | event_envelope, service_call | A second ak.agent.provision declared an agent_id that an accepted provision in this controller PCR already carries in the agent_provisioning family. The write set is empty and the earlier fact is untouched. The realm-id claim cannot catch this case: a second provision freezes a different genesis create, so it declares a different principal_control_realm_id and conflicts with nothing. Re-declaring one agent_id is how the immutable requested_scope ceiling would be widened, which zh/identity/key-management.md section 3.6.1 answers with provisioning a new Agent principal instead. See zh/identity/key-management.md section 3.6.3. |
agent_reply_not_permitted | reason_code | — | state_resolution | An Agent attempted to author ak.message.create in a scope where current controller selection and current target policy do not both enable reply_message. See zh/authz/capabilities.md §5.4 and zh/models/private-objects.md §4.1. |
agent_requested_scope_commitment_invalid | reason_code | — | event_envelope, auth_decision, service_call | The complete immutable Agent requested_scope commitment is missing from accepted-at Agent DID history, differs from the service projection, or its domain-separated digest does not verify. Pairing, Realm grant admission and agent session issuance MUST fail closed and MUST NOT treat a service-local Agent row as the authority source. See zh/identity/key-management.md section 3.6.1. |
agent_runtime_request_conflict | reason_code | — | service_call | A different stable runtime key binding was submitted while the same open pairing_request_id already has a pending runtime request. The service MUST return HTTP 409 and MUST NOT replace the current pending request, approval_request_id, or notification id. A retry with the same ak.agent.runtime_key_binding.v1 digest is idempotent and does not use this error. See zh/identity/key-management.md §3.6.2. |
agent_session_scope_refresh_required | reason_code | — | auth_decision, service_call | The immutable provision ceiling and accepted Agent key authorization both contain every operation required by the selected runtime capability, but the requested or current session scope omits one or more. The operation fails with failed_precondition and recovery is a new session constrained by both upper ceilings. See zh/identity/key-management.md §3.6.1 and agent-runtime-scope-registry.json. |
agent_signer_evidence_missing | code | 404 | both | Portable Agent signer evidence is unavailable for the authorized shared context. For callers without that context this response is indistinguishable from an unknown Agent or method. Consumers remain Unresolved and MUST NOT fall back to device directory or an ordinary-Realm MLS leaf. |
agent_signer_evidence_stale | code | 409 | both | Agent signer evidence exists but its source freshness observation or state checkpoint is too old for the target Event admission. Consumers remain Unresolved/Stale and retry without promoting the Event to Verified. |
agent_signing_key_mismatch | code | 422 | both | The Agent proof method, disclosed raw key, public-key digest, signing-binding digest, authorization Event, or controller proof do not form one exact binding. |
applet_already_registered | code | 409 | endpoint | An applet with the same identity is already registered for the realm. |
applet_e2ee_join_unauthorized | code | 403 | endpoint | An Applet, bot actor, or Applet-managed Ghost Actor attempted to join an E2EE Realm / MLS group without the independent E2EE join authorization required by ak.profile.applet_e2ee_join.v1. Ordinary message/write capability grants do not imply MLS join authority. See zh/extensions/applet-integration.md §12. |
applet_effective_scope_mismatch | code | 404 | endpoint | The applet install projection does not match the requested effective scope. |
applet_install_plan_mismatch | code | 409 | endpoint | ak.self.applet.command.install.v1 recomputed the canonical InstallPlan from the submitted Applet Package, caller-signed registration/capability-grant Events, effective_scope, and current Realm/Circle policy, and the recomputed plan_digest did not exactly match the submitted plan_digest. Server MUST fail closed and require a fresh preview/approval before formal Event admission. See zh/extensions/applet-integration.md §4b. |
applet_install_projection_incomplete | code | 409 | endpoint | The applet install projection is not yet complete. |
applet_install_required | code | 409 | endpoint | The operation requires the applet to be installed first. |
applet_managed_actor_provision_invalid | reason_code | — | event_envelope, auth_decision | An ak.applet.managed_actor.provision Event in the closed Applet managed-actor creation aggregate does not satisfy its binding rules: actor_id.account_id.station_id is not the receiving Station, the actor collides with the service or controller principal, or the provisioned independent Bot/Ghost does not match the signed authoring request, accepted Applet registration, role and effective scope. See zh/extensions/applet-integration.md. |
applet_managed_pcr_genesis_invalid | reason_code | — | pcr_genesis, event_envelope | The purpose=applet_managed_control PCR genesis Event in the Applet managed-actor creation aggregate does not cross-bind its provision Event, initial_resolution, Applet, grant and service verbatim. See zh/extensions/applet-integration.md. |
applet_managed_pcr_genesis_requires_closed_aggregate | reason_code | — | pcr_genesis, federation_transaction | A purpose=applet_managed_control PCR genesis arrived outside the closed Applet install / Ghost provisioning aggregate - an ordinary submit, a Realm bootstrap batch, or ak.peer.events.command.submit.v1. Transport and proof validity do not make it an AppletFormal admission. See zh/extensions/applet-integration.md. |
applet_namespace_conflict | code | 409 | endpoint | The applet namespace conflicts with an existing registration. |
applet_namespace_mismatch | reason_code | — | state_resolution, auth_decision | Sub-reason for a delegated-handoff Event rejection when auth_data.executed_by is not within the applet_id registration's declared service / bot DID set or namespaces.actors pattern. Prevents a grant scoped to one applet from being exercised under an unrelated applet_id. See zh/extensions/applet-integration.md §11. |
applet_namespace_pattern_invalid | code | 400 | endpoint | A namespaces.actors pattern in the submitted Applet package or derived registration violates the actor namespace pattern shape: it is not a did:webvh pattern, its SCID segment is neither '*' nor a non-empty SCID, it pins the registration's own service SCID in the SCID segment so it can never match a compliant Ghost, its host segment is not a literal equal to the host of the bare did the service_id currently resolves to, or no segment follows that host. Install preview and install commit both fail closed. See zh/extensions/applet-schema.md 2 and zh/extensions/applet-integration.md 4.1. |
applet_package_expired | code | 400 | endpoint | The submitted applet package has expired. |
applet_registration_epoch_evidence_deactivated | code | 409 | endpoint | The exact DID method version pinned by the Applet registration epoch evidence resolves successfully but is deactivated. Preview and authoring fail closed without unversioned refetch or service-local key fallback. |
applet_registration_epoch_evidence_mismatch | code | 400 | endpoint | Applet registration epoch evidence does not match the expected epoch. |
applet_registration_epoch_evidence_missing | code | 400 | endpoint | Applet registration epoch evidence is missing. |
applet_registration_epoch_signing_key_mismatch | code | 400 | endpoint | Applet registration epoch signing key does not match the registered key. |
applet_registration_unauthorized | code | 403 | endpoint | An Applet registration or transaction attempted to enter a Realm without an explicit grant from the Realm owner, Realm admin, or Realm-policy-authorized administrator actor, as checked by the Station authorization capability. Namespace claims and self-signed applet registration are insufficient. See zh/extensions/applet-integration.md §4. |
applet_revoked | code | 403 | endpoint | A revoked effective Applet install attempted a future write, transaction push side effect, delegated action, widget token use, or E2EE join. Reducers and service-call handlers MUST fail closed after ak.self.applet.command.revoke.v1 / ak.capability.revoke has taken effect. See zh/extensions/applet-integration.md §4b. Inbound transaction with a matching revoked install returns this code; never-authorized, absent or mismatched installs return applet_registration_unauthorized. |
applet_transaction_in_progress | code | 409 | endpoint | Another applet transaction is already in progress. |
approval_already_consumed | reason_code | — | event_envelope, auth_decision | An agent draft/action approval has already been consumed by a successful publish attempt. Replaying the same approval MUST fail closed instead of publishing twice. See zh/conformance/conformance-vectors.md. |
approval_carrier_unregistered | reason_code | — | auth_decision, event_envelope | A grant approval constraint or ak.policy.action approval requirement targets a capability action whose approval_requirement_eligibility is ineligible_no_registered_carrier. The governance Station rejects the authoring Event at write admission with top-level schema_violation (HTTP 422), this reason_code, and zero Event/RealmCommit/current-result effects. The canonical eligibility registry is capability-action-registry.json; the direct producers are ak.self.events.command.submit.v1 and ak.peer.events.command.submit.v1 authority_forward, with ak.self.applet.command.install.v1 covering its grant Event. See zh/authz/constraint-schema.md section 9.1 and zh/models/governance-objects.md section 3.4. |
approval_nonce_reused | reason_code | — | event_envelope, auth_decision | An approval signature whose (approval_context, approver_did, nonce) triple was already consumed by a successfully accepted target was presented for a different target. The nonce namespace is (approval_context, approver_did) and the nonce is consumed only when the target is accepted, in that same transaction: an exact replay of the already accepted target returns the original outcome, and a failed verification or an unmet quorum MUST NOT consume it early, so normal retries that collect the remaining votes still work. See zh/authz/constraint-schema.md section 9.2. |
approval_required | reason_code | — | event_envelope, auth_decision | An eligible execution was rejected because approval evidence is required and what was supplied through its registered carrier does not meet the requirement. Two independent layers can raise that requirement and both are evaluated: an approval constraint on a matched capability grant (zh/authz/capabilities.md §6 / §8, zh/authz/constraint-schema.md §9), and the Realm governance approval configuration registered as the policy_action typed current result (zh/models/governance-objects.md §3.5), which hangs off an (action token, scope) pair rather than off one grant. Either layer alone is enough to produce this reason code, and a layer writing approval_required=false only withdraws its own demand -- it never cancels the other's. The top-level error is claim_required. This is a require_review-class outcome, not a deny: supplying the missing approval signatures through the carrier named by capability-action-registry.json and retrying is the defined path. Requirements for actions without a registered carrier are instead rejected at configuration write time with approval_carrier_unregistered. |
audience_mismatch | code | 400 | endpoint | Session-grant rotation requested an audience different from the grant's bound audience. Audience MUST NOT change across rotation. |
audience_unknown | code | 404 | endpoint | A token, invite, notification, or delivery request names an audience that is unknown or not visible to the caller. |
audit_capability_incomplete | reason_code | — | auth_decision | Reading the full per-Strand watch state (including `muted` entries) requires both `ak.realm.notification.audit` and `ak.audit.accessed` capabilities; one was missing. See zh/models/strand-and-message.md §8.5. |
audit_receipt_invalidated | code | 409 | both | An auditable E2EE decrypt receipt was invalidated by backfill, witness, or state verification. |
auth_expired | code | 401 | both | Session, token, or grant has expired. |
authoring_request_expired | code | 410 | endpoint | A new authoring attempt arrived after its registered preparation expiry. Message prepare uses client created_at plus 300 seconds. The caller needs a new request identity; accepted Event validity and exact replay/recovery of an existing submission are unaffected. |
authority_cycle | reason_code | — | event_envelope, auth_decision | A ak.capability.grant would close a cycle in the authority graph, traversed as a DFS over grant_id edges taken from issuer_authority_refs entries with kind=grant. realm_root refs are rooted terminals and contribute no edge. Reducer MUST reject the grant; evaluation MUST NOT recurse without terminating. See zh/authz/capabilities.md §10. |
authority_expiry_widening | reason_code | — | event_envelope, auth_decision | For some action this grant claims, its effective window is wider than the refs that cover that action allow: it starts before the earliest effective_not_before among them, or ends after the latest effective_expires_at. The bound is evaluated per action, because a global min/max would let an action covered only by a late-window ref borrow an early one. See zh/authz/capabilities.md §10. |
authority_regrant_denied | reason_code | — | event_envelope, auth_decision | A grant was issued from a ref carrying authority_regrant_allowed=false, or declares a max_authority_depth above what its refs leave. With authority_regrant_allowed=false the child's max_authority_depth MUST be 0 and no further grant may name it. See zh/authz/capabilities.md §6 / §10. |
authority_scope_custom_unsupported | reason_code | — | event_envelope, auth_decision | A grant declared `authority_scope=custom`, which v1 does not define an evaluable semantics for. The reducer MUST reject (schema_violation) until a future profile assigns custom-scope evaluation rules. See zh/authz/constraint-schema.md §7. |
authority_scope_mismatch | reason_code | — | event_envelope, auth_decision | A child grant violated its issuer authority's `authority_scope`: `narrowing_only` requires the child resources/actions to be a strict-or-equal subset that narrows at least one axis, and `same_scope` requires the child to match the issuer authority's scope exactly. The reducer MUST reject a child that exceeds or fails to satisfy the declared narrowing discipline. See zh/authz/constraint-schema.md §7. |
authorized_grant_revoked | code | 409 | both | A pending write, cache entry, snapshot claim, or downstream grant depends on a grant that has been revoked, superseded, expired, or revoked through an ancestor grant. The dependent action MUST fail closed or be quarantined until re-authorized. |
avatar_blob_ref_invalid | code | 422 | endpoint | `ak.self.account.command.update_profile.v1` received an `avatar_blob_ref` that is not a valid Arkret Blob reference, does not resolve under the caller's profile/avatar policy, or points to a blob the server cannot authorize for profile display. Protocol profile updates MUST use `avatar_blob_ref`; `avatar_url` is not a protocol field. |
backend_unavailable | reason_code | — | event_envelope, service_call | The selected recording or transcription backend was unavailable. |
backup_revision_stale | reason_code | — | device_recovery, state_resolution | `ak.schema.key_backup.v1.source_commit_ref.realm_commit_id` does not resolve to an accepted RealmCommit on the principal control stream, or `device_generation_ref` does not equal the active `current_device_generation_ref`. A later RealmCommit on the same stream does not by itself make the envelope stale. Receivers MUST refuse to use the envelope as the primary recovery source. See zh/identity/key-management.md §7.6. |
blob_digest_mismatch | code | 422 | endpoint | Blob upload, head, download, or presign verification found that the transferred content digest does not match the declared digest. |
blob_expired | code | 410 | endpoint | The referenced blob, upload slot, or presigned blob authority has expired. |
blob_presign_invalid | code | 400 | endpoint | A blob presign request or presigned URL token is malformed, out of policy, expired, or fails integrity checks. |
blob_quota_exceeded | code | 403 | endpoint | The blob operation exceeds actor, Realm, organization, or deployment storage/bandwidth quota. |
blob_redacted | reason_code | — | state_resolution | Blob has been redacted (ak.redaction accepted) or hard-erased (ak.audit.erasure_receipt published) and is no longer fetchable. Both new presign requests and in-flight (not-yet-expired) presign URLs MUST be rejected with this code. See zh/crypto-media/media-and-blob.md §5.4.4.1. |
calendar_activation_mismatch | reason_code | — | event_envelope, schema_validation | Sub-reason for schema_violation when Strand.schema_refs and the matching metadata.fields profile subtree do not co-occur on the post-patch object, in either direction. Applies to ak.schema.calendar_event.v1 with metadata.fields.calendar. See zh/models/calendar-event.md. |
calendar_event_cancelled | reason_code | — | event_envelope, auth_decision | A new RSVP targets a Calendar Strand whose schedule status is cancelled. Historical RSVP projection is retained; new responses are refused. The generic Strand stage axis MUST NOT be reinterpreted as calendar status. See zh/models/calendar-event.md. This is an authoring-side and projection-side code for the same reason as calendar_schedule_unavailable: the cancelled status lives in the Calendar subtree plaintext. |
calendar_schedule_unavailable | reason_code | — | event_envelope, state_resolution | An RSVP or occurrence expansion was attempted while the deterministic Calendar schedule winner is encrypted_unresolved rather than available. See zh/models/calendar-event.md. This is an authoring-side and projection-side code, never a server admission gate: deciding readability requires the Calendar subtree plaintext, so gating admission on it would fork the accepted set between e2ee and plaintext Realms. |
calendar_tzdb_mismatch | reason_code | — | event_envelope, service_call, projection | The signed schedule tzdb_version is outside the receiver's executable release set declared in ServiceDescribe.calendar_tzdb_versions. The receiver MUST fail closed or mark instant projection unresolved and MUST NOT substitute a nearby or newer release. See zh/models/calendar-event.md. |
call_already_answered | code | 409 | service_call | Call answer was already accepted and the operation is not idempotent for this participant. |
call_expired | code | 410 | service_call | Call exists but its signaling window has expired. |
call_moderation_unauthorised | reason_code | — | service_call, event_envelope | A v1 moderator action (kick / ban / end-for-all, via `ak.call.signal{moderation}` or a `ak.call.state` moderation field) was attempted by an actor lacking `ak.call.moderate`. Reducer / receiver MUST reject. Moderator force-mute is deferred in v1: a mute_state{by=moderator} is unsupported_feature and an ak.call.state mute_override is schema_violation before this authorization check. |
call_not_found | code | 404 | service_call | Call does not exist or is not visible to the caller. |
call_participant_removed | reason_code | — | service_call | A participant whose `(actor_id, device_id)` has an effective kick in `call_moderation`, or whose actor has an effective ban there, attempted to re-establish a media leg or re-issue a join token. Token issuer / SFU MUST refuse; a banned actor MUST NOT rejoin until the ban is lifted. See zh/crypto-media/webrtc-signaling.md §3a. |
call_state_terminal | reason_code | — | event_envelope | An `ak.call.state` event attempted to transition `state_transition.from` out of a terminal value (`ended` / `missed` / `failed` / `cancelled`). Call lifecycle is monotonic; the reducer MUST `failed_precondition`. Capture lifecycles use orthogonal per-segment transition typed results. See zh/crypto-media/call-state.md §4.2. |
call_state_transition_invalid | reason_code | — | event_envelope | A `ak.call.state` event requested a `state` transition from a non-terminal state that is not listed in the legal-successor table (transitions out of a terminal state use `call_state_terminal` instead). The reducer MUST `failed_precondition`. See zh/crypto-media/call-state.md §4.2. |
cannot_pair_current_device | code | 409 | endpoint | The current device cannot pair with itself. |
capability_denied | code | 403 | both | Capability, policy, or visibility rules deny the request. |
cas_conflict | code | 409 | both | An optimistic concurrency precondition failed because the authority-committed typed current result or stream head no longer equals the submitted expectation. The caller must read current state and create a new signed Event; exact retry retains the original identity. |
causal_conflict | code | 409 | both | An application-level referenced object, revision, or domain transition is incompatible with current authority-committed state. This code does not describe an Event predecessor graph. |
cbor_bounds_invalid | reason_code | — | encoding, schema_validation | A hand-written deterministic CBOR structure (e.g. the mls_governance_binding GroupContext extension) violates v1 decode bounds: a single array/map declares or contains more than 65536 items, or a string / byte string / array / map header declares a length or item count exceeding the remaining input bytes (capacity bomb). Decoder MUST reject (top-level schema_violation) before allocating buffers sized from the declared length. See zh/conformance/scalability-constraints.md section 2. |
cbor_not_deterministic | reason_code | — | encoding, schema_validation | A hand-written CBOR structure violates RFC 8949 section 4.2 deterministic encoding requirements: indefinite-length items (map 0xbf, array 0x9f, string 0x5f/0x7f), non-minimal length encoding, or unsorted map keys. Receiver MUST reject (top-level schema_violation) instead of normalising. See zh/conformance/scalability-constraints.md section 2 and zh/crypto-media/encryption-and-audit.md section 2.5.3. |
challenge_expired | reason_code | — | event_envelope, auth_decision | A challenge proof was submitted whose issuance is older than `max_proof_age`. The reducer MUST reject with failed_precondition and MUST NOT auto-renew; the applicant must obtain a fresh challenge_id. See zh/governance/join-policy.md §3.1 and §4. |
challenge_failed | reason_code | — | event_envelope, auth_decision | A join-policy challenge gate answer (e.g. CAPTCHA / proof-of-work / knowledge challenge) carried by a membership admission request failed verification. |
challenge_proof_invalid | reason_code | — | auth_decision, service_call | A runtime challenge proof attached to `ak.member.state{join}.gate_proofs[]` fails verification (signature / freshness / verifier domain). Freshness here is the replay/binding freshness carried by the proof itself; a proof issued beyond max_proof_age is challenge_expired instead, and the two reasons are mutually exclusive. See zh/governance/join-policy.md §4. |
circle_already_terminal | reason_code | — | state_resolution | Sub-reason for failed_precondition when a Circle lifecycle write targets a Circle that is already tombstoned or otherwise terminal. See zh/models/common-fields.md §5.1 and zh/models/circle.md §9. |
circle_count_exceeded | reason_code | — | event_envelope, state_resolution | Reducer rejected a `ak.circle.create` exceeding the per-Realm active Circle cap, or a `ak.circle.member.state -> join` that would exceed the per-actor active MLS-backed Circle cap. Caps bound cascade/delivery fanout and the M+R MLS-rotation amplification of a single membership change (zh/conformance/scalability-constraints.md §5, zh/models/circle.md §10). |
circle_member_must_be_realm_member | reason_code | — | state_resolution | Sub-reason for failed_precondition on ak.circle.member.state -> join when, in the accepting cut, the target actor's parent Realm member_state current is not `join` or its typed revision differs from the payload's parent_membership_revision. Circle.members ⊆ Realm.members is a hard invariant bound to the exact current parent join. See zh/models/circle.md §9.1. |
circle_not_active | reason_code | — | state_resolution | Sub-reason for failed_precondition when scope_circle_id points at a Circle whose state is archived or tombstoned. See zh/models/circle.md §6.1. |
circle_not_archived | reason_code | — | state_resolution | Sub-reason for failed_precondition when ak.circle.restore targets a Circle whose current lifecycle state is not archived. See zh/models/common-fields.md §5.1 and zh/models/circle.md §9. |
circle_realm_mismatch | reason_code | — | schema_validation, state_resolution | Sub-reason for schema_violation when an object's scope_circle_id references a Circle whose realm_id does not match the object's realm_id. See zh/models/circle.md §6.1. |
circle_short_name_taken | reason_code | — | schema_validation, state_resolution, service_call | Circle creation or display update failed the reducer-enforced case-insensitive uniqueness of display.short_name within (realm_id, short_name). See zh/models/circle.md §4. |
claim_failed | code | 400 | endpoint | An atomic one-time key, pre-key, or KeyPackage claim failed. On privacy-sensitive claim surfaces this code deliberately collapses target absence, visibility, inventory, consent, policy, capability, freshness, and abuse-control failures into one non-enumerable outward result. |
claim_generation_mismatch | reason_code | — | crypto, auth_decision | An MLS Welcome / KeyPackage claim binds a device generation that does not equal the receiver's current accepted generation. Receivers MUST reject before admitting the Welcome or key material. See zh/crypto-media/encryption-and-audit.md §2.6 and zh/crypto-media/device-lifecycle.md §14. |
claim_invalid | reason_code | — | event_envelope, auth_decision | A claim, attestation, or invite / binding proof submitted for membership or invitation admission is malformed, unverifiable, or fails policy checks. |
claim_required | code | 403 | both | A required claim, attestation, or presentation is missing. |
conflict | code | 409 | both | A generic state conflict occurred. |
consent_required | code | 403 | service_call | The holder requires explicit user consent before disclosing the requested presentation. |
consent_revoked | reason_code | — | auth_decision | Authorization outcome when a cached consent decision is re-evaluated and the underlying consent has been revoked; the stale cache entry MUST NOT authorize the action. See zh/conformance/conformance-vectors.md §3.5 (ak.vector.consent.cache_invalidation.v1). |
consent_withdrawn | reason_code | — | event_envelope, state_resolution | Required recording or transcription consent was withdrawn. |
contact_lineage_conflict | code | 409 | endpoint | A Contact fact forks an issuer-local version, skips a version, references a non-current predecessor, reuses a consumed request ref, or attempts to revive a tombstoned generation. The conflicting fact is quarantined. |
contact_request_expired | code | 409 | both | Sub-reason for failed_precondition when ak.self.contact.command.respond.v1 or ak.self.contact.command.reject.v1 targets a contact request older than contact_request_pending_ttl. The request projection is expired and no terminal response may be authored from it. |
contact_request_not_pending | code | 409 | both | Sub-reason for failed_precondition when ak.self.contact.command.respond.v1 or ak.self.contact.command.reject.v1 targets a request that has already been accepted, rejected, tombstoned, withdrawn, or otherwise left the pending state. |
contact_scope_stale | code | 409 | endpoint | An existing Contact binding is discoverable but the source-signed issuer head checkpoint/current lease is stale, unknown or incomplete. Contact-based create/send fails closed while coordinates remain visible to authorized participants. |
continuity_evidence_unavailable | code | 409 | endpoint | A required bilateral continuity checkpoint, predecessor checkpoint or uncompressed tail segment is not currently available. The operation writes nothing and may be retried only after importing or retrieving the exact portable evidence; no gap is inferred or skipped. |
continuity_invalid | code | 409 | endpoint | Bilateral continuity evidence has an invalid signature, root/participant mismatch, broken or trimmed edge, rollback, same-sequence fork, accumulator mismatch or one-sided checkpoint. The operation writes nothing and MUST NOT downgrade this result to evidence retrieval. |
controller_signed_event_required | code | 400 | endpoint | An Agent operation that must preserve controller authorship omitted the required controller-signed durable Event proof. The service MUST NOT synthesize, service-sign, or directly project the missing controller fact. The controller must author and submit the exact closed Event required by the operation. See zh/identity/key-management.md §3.6.1 Lifecycle. |
credential_expired | code | 410 | service_call | A credential matching the presentation request exists but is expired. |
credential_not_found | code | 404 | service_call | The holder has no credential matching the authorized presentation request. |
cross_domain_replay_rejected | reason_code | — | identity_creation, proof_verification | The signed proof audience, origin or trust_domain does not match the current request context; the verifier rejects it before any state transition. |
cross_realm_structural_relation | reason_code | — | event_envelope, auth_decision | A structural `contains` Relation was submitted that would cross Realm boundaries. Structural containment (Board → List → Strand / Space hierarchy) MUST stay within a single Realm; cross-Realm links use the dedicated `ak.relation.*` non-structural kinds. See zh/models/relation.md §4. |
current_did_authority_unavailable | code | 503 | both | A call site explicitly registered as current-DID-dependent cannot obtain fresh current authority. Unrelated human PCR operations remain available. |
cursor_expired | code | 410 | endpoint | The opaque cursor (ak:cursor:...) has a valid shape but its `x` expiry is in the past. Client MUST request a fresh cursor (initial /account/subscribe, /snapshot/head, or new write barrier). |
cursor_integrity_invalid | code | 400 | endpoint | Cursor integrity check failed for the v1 stateful opaque handle (client-sync §12.1, canonical body `{v, purpose, issued_at, expires_at, h}`): the `h` handle is unknown / revoked / expired / cross-bound, or its stored binding (principal, device, service, filter_digest, purpose) does not match the authenticated request. Distinct from cursor_expired (TTL) and cursor_unrecognized (cross-service portability miss). Client MUST clear local cursor cache and restart from initial /account/subscribe. |
cursor_invalid | code | 400 | endpoint | The supplied cursor fails syntax, schema, purpose, binding, or integrity validation before the operation can advance state. Welcome recipient discovery also uses this code when its bound current eligibility revision changed or its frozen window is no longer retained; unrelated RealmCommit advancement alone does not invalidate it. |
cursor_revoked | code | 410 | endpoint | Cursor integrity is valid but the issuing service has explicitly revoked this cursor authority before TTL expiry. Endpoint MUST NOT advance subscription position, barrier wait, or dropped recovery state; caller MUST restart from a fresh cursor. To-device queue deletion is decoupled from cursors and unaffected (client-sync.md §10.1). |
cursor_unrecognized | code | 400 | endpoint | The cursor decoded successfully but cannot be used by this service because it is a stateful handle issued by another service. Caller MUST restart sync from a fresh cursor. |
deactivation_federation_incomplete | reason_code | — | account_status, federation_transaction, state_resolution | Account deactivation could not be acknowledged by every peer Station inside deactivation_propagation_window_ms. Source services MUST keep deactivation fanout retrying and pause new Realm onboard, session/device grant, and KeyPackage publication for the principal. |
decryption_failed | reason_code | — | client_sync | Recipient permanently cannot decrypt; epoch is unrecoverable on this device under current keys. |
decryption_pending | reason_code | — | client_sync | Recipient cannot decrypt the targeted MLS epoch yet; client MUST surface a placeholder and continue retrying within the configured window. |
delegation_revoked | reason_code | — | auth_decision, service_call | An applet/service call used a delegated device session that the deactivation/lock fanout revoked (ak.applet.registration delegated devices). The call MUST fail closed. See zh/identity/account-lifecycle.md §7.1. |
delivery_target_unreachable | reason_code | — | event_envelope, service_call | Sub-reason carried by the ak.invite.revoke that moves a pending Invite to target_state=send_failed: the delivery service could not reach the private invite delivery target after its retry budget. It is a delivery diagnostic only and MUST NOT leak the 3PID plaintext, the invite token or a verification code. See zh/models/governance-objects.md section 5 and zh/sync/third-party-invites.md section 6.1. |
dependency_missing | code | 409 | both | One or more exact Event, RealmCommit, predecessor, proof, or other signed dependencies are absent. The response MUST identify the bounded missing set in the operation's closed details/item shape. This is recoverable only through bounded canonical backfill/resolve followed by a new evaluation; it is not authorization denial or service unavailability. Dual-registered with the per-item/federation-transaction reason code. |
device_already_authorized | code | 409 | endpoint | The device is already authorized. |
device_authorized_principal_control_realm_mismatch | reason_code | — | auth_decision, state_resolution | A non-bootstrap ak.device.authorize event was submitted outside the principal's bound principal_control Realm, or the Realm purpose/profile/created_by does not match the device owner and issuer principal. Reducer MUST fail closed. See zh/identity/key-management.md §4.1. |
device_directory_unavailable | reason_code | — | service_call | The requester's own Station could not obtain or verify a current attested device projection for an exact (account_id, device_id) on this call. It reports only the fetching Station's result, never target state, and MUST NOT be rendered as an omitted row or an empty device list. See zh/crypto-media/device-lifecycle.md section 8.2.1. |
device_generation_fenced | code | 409 | both | The receiver knows an authenticated device-generation revocation or the security command fails the current generation revision check. New affected live submissions are fenced; ordinary historical eligibility is recomputed from the signed authority context and authenticated closures, not the receiver arrival time. |
device_message_id_conflict | reason_code | — | event_envelope, client_sync | A to-device retry reused the same device_message_id with different canonical target content. The queue service MUST return duplicate_conflict with this reason_code and MUST NOT enqueue a replacement message. Scheduled-send plan convergence is keyed independently by scheduled_send_id and account-data CAS; durable ak.message.create identity conflicts are keyed only by the content-bound Event.event_id. |
device_reanchor_authority_mismatch | code | 409 | both | A device re-anchor receipt scope, recovery session or transaction snapshot selects an account-local lineage or device generation that does not exactly match the covered ak.device.reanchor payload. |
device_reanchor_authorize_mismatch | code | 409 | both | A device re-anchor completion authorization does not match the expected re-anchor authorization. Dual-registered as a service code and a reason_code (see reason_codes[]). |
device_reanchor_checkpoint_mismatch | code | 409 | both | A device re-anchor completion carries a checkpoint that does not match the recomputed device checkpoint. Dual-registered as a service code and a reason_code (see reason_codes[]). |
device_reanchor_conflict | code | 409 | both | Concurrent device re-anchor completions conflict on the same principal generation state. |
device_recovery_generation_mismatch | code | 409 | endpoint | Device recovery proof or authorization references a device generation that does not equal the principal's current accepted device generation. |
device_result_unavailable | reason_code | — | service_call | Single non-enumerating device directory failure. Absent, invisible, unrelated, revoked, fenced and policy-denied targets MUST all use this one value so a caller cannot probe device existence or revocation state. See zh/crypto-media/device-lifecycle.md section 8.2. |
device_revocation_pending | code | 409 | endpoint | The exact device generation is blocked by one or more durable accepted ak.device.revoke proposals that have not been terminally rejected by a confirmed RealmCommit command result or covered by an accepted RealmCommit. Timeout and cache eviction do not clear this state. |
device_revoked | code | 409 | endpoint | The device has been revoked. |
device_unauthorized | code | 403 | endpoint | The device is not authorized for the requested operation. This is the typed local/self-surface outcome when no complete current accepted device authorization can be derived; anti-enumerating peer device-revocation checks instead return a signed authority_mismatch decision. A malformed row that claims current verified authorization while omitting its required Event/generation binding is an internal projection-integrity failure, not this ordinary authorization outcome. |
device_unknown | code | 404 | endpoint | The referenced device id is unknown, not visible, or no longer active for the target principal. |
did_already_exists | code | 409 | endpoint | A DID operation attempted to create or register a DID that already exists under the registry's uniqueness rules. |
did_method_successor_invalid | code | 409 | both | The proposed current resolution is not a valid method-native same-core successor. A valid PCR author proof alone cannot advance it. |
did_not_found | code | 404 | endpoint | The requested DID cannot be found under the active identity registry and anti-enumeration policy. |
did_proof_required | code | 401 | both | An explicitly selected DID-root authentication factor or sensitive account-control transition requires its operation-specific fresh DID proof. Soft logout does not use this code as a refresh challenge: it requires full reauthentication to a fresh AccountHandoff or an explicit account-control action. |
did_revoked | code | 410 | endpoint | The requested DID exists in historical registry state but has been deactivated, revoked, or superseded. |
did_unknown | code | 422 | both | A DID cannot be resolved or validated under the active resolver policy. |
digest_mismatch | code | 422 | both | A declared digest does not match the transferred content. |
direct_conversation_binding_invalid | reason_code | — | event_envelope, auth_decision, state_resolution | The ak.self.events.command.submit.v1 Direct Conversation binding-integrity stage rejected an ak.direct_conversation.bound Event because its issuer, pair key, authorization basis, Realm role, exact two-member set, main Strand, founding unit digest, or founding MLS references did not match accepted authoritative facts. The rejection occurs before result application, is atomic, and writes nothing; see contract-registry.json operation_registry.direct_conversation_admission_mappings. |
direct_conversation_founding_unit_invalid | reason_code | — | event_envelope, service_call, federation_transaction | A Direct Conversation founding unit is not the closed caller-authored four-Event unit. Causes include a count other than four, wrong wire order, a missing peer or founder ak.member.state{join}, a missing ak.strand.create, a fifth Event, a mixed actor/pair/profile/Realm, any envelope field naming a predecessor Event inside the unit, an envelope realm_id that is not retype(events[0] event_id), a main_strand_id that is not retype(events[3] event_id), a founding_unit_digest that does not match the recomputed value, or any request field asserting a service-allocated identifier, reservation handle or materialization draft, or a genesis whose initial_join_rule, initial_discoverability or initial_history_access is not the create-locked closed, invite_only or since_join. Carried under failed_precondition on both the self and the peer path; the whole unit is rejected with zero writes. See zh/identity/contact-and-direct-conversation.md sections 5.5 and 6.1. |
direct_conversation_invite_forbidden | reason_code | — | event_envelope, auth_decision | The ak.self.events.command.submit.v1 active-profile invite guard rejected an ak.invite.create or ak.invite.third_party Event targeting a Direct Conversation. A pair-external candidate is classified earlier as direct_conversation_third_party_member_forbidden; the atomic founding peer join is not an invite. The rejection writes nothing; see contract-registry.json operation_registry.direct_conversation_admission_mappings. |
direct_conversation_member_count_invalid | reason_code | — | event_envelope, auth_decision, state_resolution | The ak.self.events.command.submit.v1 exact-two profile gate rejected a new write because the Direct Conversation binding or authoritative membership projection did not resolve to exactly two distinct principal participants. Existing stable conversations are returned read-only by ak.self.direct_conversation.read.resolve.v1 as suspended with blocker member_count_invalid, never as a write rejection. The rejection writes nothing; see contract-registry.json operation_registry.direct_conversation_admission_mappings. |
direct_conversation_pair_materialization_conflict | reason_code | — | event_envelope, auth_decision, state_resolution | A second accepted Direct Conversation Realm was observed for the same pair_key while both carried apparently valid founder admission, four exact Events, four consecutive source RealmCommits and founding-authority evidence, indicating slot, cutover-fence or signature equivocation by a trusted current Station. Both Realms freeze new Message, membership, policy, MLS and binding writes and all evidence is retained; implementations MUST NOT pick a winner by Realm-token lexical order or arrival order, tombstone either Realm, or migrate history. See zh/identity/contact-and-direct-conversation.md §5.7. |
direct_conversation_participant_authority_denied | reason_code | — | event_envelope, auth_decision, service_call | The current governance Station's active ak.authority.direct_conversation_participant.v1 evaluator rejected an Event-mapped allowlisted action because it could not establish every required immutable binding, exact participant, active membership, Realm/Strand/current scope-derived MLS group-and-epoch cross-binding, lifecycle, resource, directional Contact, device, or Agent input. The ak.self.events.command.submit.v1 rejection exposes only this non-enumerating reason and writes nothing. Encrypted Signal product actions use their own outer admission carrier and recipient policy, never this Event reason. Consent, created_by, a local projection row, Realm owner aggregation, or an arbitrary Event/current result cannot substitute; see contract-registry.json operation_registry.direct_conversation_admission_mappings. |
direct_conversation_root_mask_violation | reason_code | — | event_envelope, auth_decision | The ak.self.events.command.submit.v1 technical-root phase-mask stage rejected an operational, grant, member-governance, policy, or terminal action outside the exact active Direct Conversation founding/materialization mask. Root owner aggregation cannot bypass participant authority or target the other participant. The rejection writes nothing; see contract-registry.json operation_registry.direct_conversation_admission_mappings. |
direct_conversation_slot_already_committed | reason_code | — | service_call, federation_transaction | The founder's current Station already closed its local (founder_id, trust_domain_id, pair_key) founding slot with a different unit, so this unit is refused with zero writes. Carried under conflict. The caller MUST re-resolve the existing coordinates through ak.self.direct_conversation.read.resolve.v1 instead of authoring another unit; the service MUST NOT accept a second unit, degrade it to a partial acceptance or quarantine it. A byte-identical replay of the committed unit is not this code: it returns the same four byte-identical source RealmCommits without changing the fourth committed_at. See zh/identity/contact-and-direct-conversation.md section 5.5. |
direct_conversation_space_forbidden | reason_code | — | event_envelope, auth_decision | A Space create/update/parent/archive/restore/tombstone operation targeted a Realm carrying ak.profile.direct_conversation_realm.v1. Space containers are not permitted in this constrained Realm role. |
direct_conversation_terminal_forbidden | reason_code | — | event_envelope, auth_decision, state_resolution | The ak.self.events.command.submit.v1 profile terminal guard rejected ak.realm.destroy or ak.realm.tombstone against the canonical Direct Conversation Realm. DM coordinates are permanent and successor-free. Reversible archive/freeze and their reverse transitions remain subject to ordinary authority and do not match this reason. The rejection writes nothing; see zh/identity/contact-and-direct-conversation.md §8.1 and contract-registry.json operation_registry.direct_conversation_admission_mappings. |
direct_conversation_third_party_member_forbidden | reason_code | — | event_envelope, auth_decision | The ak.self.events.command.submit.v1 candidate-pair membership stage rejected an invite or join whose candidate ActorId was not one of the immutable Direct Conversation binding's two exact ActorIds. This reason has precedence over direct_conversation_invite_forbidden when both predicates match. Group expansion requires a new ordinary Collaboration Realm. The rejection writes nothing; see contract-registry.json operation_registry.direct_conversation_admission_mappings. |
direct_conversation_unavailable | code | 409 | both | Opaque failed_precondition sub-reason for direct-conversation resolution when the peer, either directional Contact head/scope, trust-domain binding, or no-create binding state cannot be disclosed. Consent is not queried. Requester-visible status, body and timing MUST NOT distinguish those causes; detail is holder-private audit only. |
direct_download_disallowed_presign_forbidden | reason_code | — | authz, service_call | A presigned blob URL was requested for a Realm-owned blob whose current effective asset policy does not explicitly set direct_download_allowed=true. Missing, unverifiable, non-effective, omitted, or false policy state all fail closed. The service MUST deny presign and require authenticated fetch or an authorized proxy path. |
discovery_failed | code | 404 | endpoint | Discovery of the requested resource failed. |
discussion_track_disabled | code | 409 | both | The target Strand discussion track is disabled. |
duplicate_clause_claim | code | 422 | both | An SDK conformance claim repeats the same stable clause_id; each applicable clause must appear exactly once. |
duplicate_conflict | code | 409 | both | The same idempotency key, non-Event stable identifier or Event identity was reused with different canonical content or canonical Event bytes. An Event Envelope whose carried event_id does not match its own digest uses event_id_digest_mismatch; confirmed full-hash collision evidence uses witness_disagreement. |
e2ee_key_source_unauthorised | reason_code | — | service_call | A backend media SDK supplied an SFrame / frame encryption key from a source other than the Arkret MLS exporter (label `ak.rtc-frame-key/v1`). Clients MUST reject and refuse to publish / subscribe media. Closes the attack where backend cloud key escrow could intercept ostensibly-E2EE media. See zh/crypto-media/media-service-binding.md §8.1. |
e2ee_required | code | 403 | service_call | Realm or call policy requires E2EE and the requested media path did not satisfy it. |
effective_scope_reducer_managed | reason_code | — | schema_validation | Sub-reason for schema_violation when an actor-supplied payload illegally carries `effective_scope` where the object schema reserves that name for a read-only materialized projection. Both surfaces are decided by schema: the create shape bans the member (event-payload.schema.json#/$defs/relation_create_object), and the update shape bans the `effective_scope` / `effective_scope.*` patch paths (#/$defs/relation_update_payload), registered in registry/reducer-managed-path-registry.json. The Event envelope instead requires producer-signed `scope_ref`; the receiver derives the scope from payload and frozen pre-state, verifies exact equality, and only then may copy it into the object's effective_scope projection. See zh/models/circle.md §6.2. |
egress_policy_denied | reason_code | — | authz, federation_transaction, service_call | An outbound Applet, MIMI, or federation transfer would emit plaintext or derived content without the egress grant, data-class allowance, or destination policy required for that transfer. It also covers SSRF address-class denial: a server-side fetch whose resolved target (after redirect / Alt-Svc) hits a forbidden address class such as cloud metadata, internal, or loopback is rejected with this reason, for example MIMI proxy_download. The sender MUST reject the transfer before releasing content. See zh/sync/api-conventions.md §11.2 and zh/extensions/mimi-interop.md §11. |
enclave_no_upstream_proxy_for_external | code | 403 | endpoint | No upstream proxy is configured for external egress from the enclave. |
enclave_not_trusted | code | 403 | endpoint | The enclave is not trusted for the requested operation. |
epoch_mismatch | code | 409 | both | An encrypted authoring request references an epoch, group_state_ref or key_access_revision that differs from the ready current MLS group. HTTP 409. After verifying and acquiring current local MLS state, the sender re-encrypts into a new request; an exact retry of the old request does not re-encrypt. If the current scope itself awaits a winning Commit, failed_precondition with epoch_update_required takes precedence. Historical accepted Event replay and peer committed replication use their accepted historical binding. |
epoch_update_required | reason_code | — | crypto, service_call, state_resolution | A membership change on the scope's own stream advanced key_access_revision and the current MLS epoch does not yet have a winning ak.mls.commit whose governance_binding covers that revision. Clients MUST pause new application messages for the scope until the effective epoch catches up. Endpoint authorization and policy changes never produce this reason. See zh/crypto-media/encryption-and-audit.md §2.4.1. |
erasure_pending_is_terminal | reason_code | — | account_status, event_envelope, state_resolution | An Account Authority issuer-ledger mutation attempted to create a successor after `erasure_pending`. The state is terminal because erasure physically destroys data; the current-head transaction and every receiver MUST reject the successor and retain the terminal record. See zh/identity/account-lifecycle.md §3. |
erasure_receipt_authority_invalid | reason_code | — | account_status, identity_resolution, service_call | The erasure receipt issuer, signing verification method, service transport identity or accepted-at authority chain does not establish authority for the claimed erasure scope. The receiver MUST reject before deletion or durable acknowledgement. |
erasure_receipt_proof_invalid | reason_code | — | account_status, identity_resolution, service_call | One or more required erasure receipt proofs fail canonical transcript, signature, threshold or validity-window verification. The receiver MUST reject before deletion or durable acknowledgement. |
erasure_receipt_stub_binding_mismatch | reason_code | — | account_status, identity_resolution, service_call | The retained stub carried with an erasure receipt does not bind the receipt scope, subject, terminal status or required verification coordinates. The receiver MUST reject before deletion or durable acknowledgement. |
erasure_receipt_stub_digest_mismatch | reason_code | — | account_status, identity_resolution | Retained erasure stub bytes do not match retained_stub_digest in the erasure receipt. Verifier MUST reject the receipt and treat the erasure as not completed (fail closed). |
erasure_request_already_pending | reason_code | — | account_status | Sub-reason for failed_precondition on ak.gate.account.command.request_erasure.v1: the account already holds a live self-initiated erasure intent whose erasure_pending AccountStatusRecord has not yet been signed. The client MUST NOT submit a second distinct request; it either awaits the recorded intent or withdraws it inside the deployment-granted withdrawal window. Once the record is signed, requests fail at authentication with account_erased instead. See zh/identity/account-lifecycle.md section 8.1. |
event_id_digest_mismatch | reason_code | — | event_envelope | Sub-reason for schema_violation when the carried event_id does not equal the value re-derived from the Event's own canonical content per zh/conformance/encoding.md section 4.0. Receivers MUST re-derive and compare before using event_id for deduplication, indexing, routing, idempotency or authorization, and MUST NOT report this as proof_invalid: the distinct code is what localises cross-implementation canonical-JSON divergence and what prevents a forged event_id from entering the duplicate_conflict quarantine path. |
evidence_recipient_mismatch | reason_code | — | moderation_decision, moderation_report | A moderation evidence package's encrypted_to recipient does not match the declared recipient_public_key_ref binding. The submission MUST reject. See governance/content-moderation.md. |
expired_invite_token | reason_code | — | state_resolution | Invite token's expires_at has passed when claim is attempted. Internal-only reason code; the wire response MUST be the unified non-enumerable `not_found` per zh/sync/third-party-invites.md §6.1. |
external_invite_actor_mismatch | code | 403 | endpoint | The external invite actor does not match the authenticated actor. |
external_rate_limited | reason_code | — | service_call | Bridge / applet transaction failed because the external upstream service rate-limited the request, distinct from the local `rate_limited` (this service's own limit). Carried as a bridge_error error_code with error_class=rate_limit; the caller MAY retry after retry_after_ms. See zh/extensions/applet-schema.md §7. |
external_user_no_main_access | code | 403 | endpoint | The external user has no access to the main deployment surface. |
failed_plane | code | 412 | both | authority-commit plane invariant failed: the registered reducer projection for a Event targeted a data-plane typed current result, or a Event targeted a control-plane typed current result. Terminal failure state; the write MUST NOT be bypassed as cas_conflict. See zh/authz/event-auth-state-resolution.md §13 and zh/sync/service-http-binding.md (write outcomes). |
failed_precondition | code | 409 | both | Reducer state-machine or typed CAS precondition failed. A stale typed revision writes nothing and does not carry a generic current/current_result Problem extension; authorized current is obtained only through a separately registered read. Other failures may carry a registered reason_code in the {strand,space,morph}_not_active / _not_archived or {strand,space,morph,message,relation}_already_terminal families, or an object-specific reason such as space_parent_cycle. See zh/models/common-fields.md §5.1 and zh/sync/current-results.md §2. |
federation_actor_origin_denied | code | 403 | service_call | The federated actor origin was rejected by inbound policy. |
federation_authority_mismatch | reason_code | — | federation_transaction, service_call | HTTP Message Signature @authority / target URI host does not match the resolved service endpoint for Destination-Service-ID, or the Destination-Service-ID is not authorized by Realm policy for the requested federation operation. Receiver MUST reject before processing events. |
federation_interop_track_only | code | 501 | service_call | The requested federation surface is interop-track only. |
federation_origin_denied | code | 403 | service_call | The federated origin was denied. |
federation_private_read_rail_local_only | code | 501 | service_call | The private read rail is local-only and not federated. |
federation_trust_domain_mismatch | reason_code | — | federation_transaction, service_call | Destination-Trust-Domain does not equal the receiver deployment's ServiceDescribe.trust_domain, or does not match the receiving Realm's trust_domain (zh/sync/federation.md §3.2). Internal audit-only reason; the wire response MUST be the unified minimal-disclosure authentication failure envelope. |
first_backup_gate_unsatisfied | code | 409 | endpoint | The recovery-material gate requirements (first accepted RealmCommit and genesis recovery policy) are not satisfied. |
focus_mismatch | reason_code | — | service_call, auth_decision | Media token exchange (ak.self.call.media.exchange.issue_token.v1) requested a `focus_id` different from the already-committed `ak.call.state.session_focus`. Token issuer MUST reject; clients MUST re-target the established focus rather than retrying with the original preference. See zh/crypto-media/media-service-binding.md §3 and §5. |
focus_unavailable_for_client | reason_code | — | service_call | Client cannot use the committed `session_focus` (e.g. focus not in local `foci_preferred[]`, region restricted, capability mismatch). Client MAY fail closed without joining the call rather than silently degrading; clients MUST NOT pick a different focus to bypass `session_focus_no_split_brain`. See zh/crypto-media/media-service-binding.md §5. |
founding_device_commitment_mismatch | reason_code | — | pcr_genesis | The FoundingDeviceDescriptor, authorize payload, device/HPKE digests, algorithms or root creation transcript are not byte-for-byte consistent. |
franking_proof_unavailable | code | 503 | both | E2EE franking proof cannot be produced for the requested ciphertext (sender did not include franking sidecar). See zh/governance/content-moderation.md §3.4. |
franking_tampered | code | 400 | endpoint | The franking tag is tampered or does not verify. |
gate_check_failed | reason_code | — | auth_decision, state_resolution | External applicant-facing generic join gate failure. Wire response MUST NOT reveal whether a claim was absent, revoked, issuer-unreachable, parent-membership-missing, or challenge-invalid; detailed diagnostics are audit/reviewer-only. See zh/governance/join-policy.md §5. |
governance_binding_mismatch | reason_code | — | event_envelope, state_resolution, federation_transaction | An MLS Commit's mls_governance_binding GroupContext extension does not match the accepted key-access state, Event payload or active epoch chain: scope/base/epoch fields disagree, the unsigned monotonic key_access_revision counter differs from the Station's accepted key-access revision, or extension bytes differ from the Event payload. The receiver MUST reject the Commit - and, on a federation push, the batch - rather than advance an epoch under a forged or stale binding. See zh/crypto-media/encryption-and-audit.md §2.5. |
grant_already_consumed | code | 400 | endpoint | Session-grant rotation targeted a grant that was already single-use consumed (rotated). Treated as a credential-compromise signal; the rotation chain SHOULD be terminated. See account-lifecycle §4.1. |
grant_exceeds_issuer_authority | reason_code | — | event_envelope, auth_decision | An `ak.capability.grant` attempts to grant actions[] / resources[] that exceed the union of its `issuer_authority_refs[]` at the issuing authority commit basis. Holding the `ak.capability.grant` action alone does not permit minting authority the issuer does not itself hold; reducers MUST fail closed (schema_violation for actions/resources over-scope, failed_precondition when the issuer does not hold the required upper bound at that basis). See zh/authz/capabilities.md §3.2. |
grant_relinquish_not_subject | reason_code | — | event_envelope, auth_decision | A ak.capability.relinquish named a grant whose subject is not the actor. Relinquish is subject-only precisely so it needs no revoke capability; allowing any other actor would turn it into an unauthorized revocation. The authority-root typed current result is not a grant and can never be a relinquish target. The rejection MUST NOT disclose whether the target exists. |
grant_revoke_not_authorized | reason_code | — | event_envelope, auth_decision | A ak.capability.revoke passed ordinary action authorization but failed the target guard: the actor is neither the target grant's issuer nor the current root controller of the target grant's own realm_id. Controlling some upstream root reachable through authority_root_refs is deliberately not enough — a co-owner or sibling MUST NOT be able to revoke an upstream or peer grant by holding ak.realm.owner. The rejection MUST NOT disclose whether the target exists or which Realm it belongs to. |
grant_revoked_before_event_checkpoint | reason_code | — | auth_decision | The capability grant cited as authority was revoked at a checkpoint causally preceding this event; reducer rejects. |
grant_revoked_upstream | reason_code | — | auth_decision, state_resolution | A child grant or Event depends on a parent grant that is locally known to be revoked, superseded, expired, or tombstoned. Reducers MUST fail closed immediately. See zh/authz/capabilities.md §10.3. |
grant_validity_window_empty | reason_code | — | auth_decision, event_envelope | A capability grant's normalized effective validity window is empty: effective_not_before >= effective_expires_at after intersecting its temporal constraints. Reducer MUST reject the grant. See zh/authz/capabilities.md §6.1. |
handle_holder_acceptance_missing | reason_code | — | auth_decision, service_call | A restricted HandleClaim status view was presented as status=verified while its immutable claim core omitted or invalidated the required holder_acceptance proof over the exact claim_digest (including claim.subject_account_id, claim.handle and claim.audience). Verifiers MUST reject the entire status view: it MUST NOT enter the verified candidate set, be displayed as verified, or drive grant conditions, roster strong attribution or AccountId targeting. This closes issuer-unilateral impersonation within the issuer's audience. See zh/identity/identity-handles.md §6. |
handle_homograph_forbidden | reason_code | — | schema_validation, service_call | Handle registration collided with the same authority-local handle-namespace UTS #39 skeleton index or failed the authority's declared Highly Restrictive registration policy. This is registration policy, not canonical equality; the skeleton never enters wire or proof bytes. See zh/identity/identity-handles.md §17. |
harassment | reason_code | — | moderation_report | Standard moderation reason: harassment. |
hate_speech | reason_code | — | moderation_report | Standard moderation reason: hate speech / targeted attacks against a protected group. |
historical_did_evidence_invalid | code | 422 | both | Registration-time or Event accepted-at DID evidence cannot be replayed at its frozen historical boundary. Current DID state MUST NOT be substituted. |
historical_only | code | 200 | endpoint | Historical-only diagnostic for endpoints that explicitly register this outcome; it does not bypass current authentication and is not emitted by the closed peer event submit path after origin service-key revocation. |
history_not_visible | code | 403 | both | The requested Event range, backfill window, or preview field is not visible to the caller under the target Event's T0 history_access and the current safety policy. Non-enumerating surfaces MAY map this to not_found. See zh/governance/history-visibility.md. |
hlc_logical_overflow | code | 503 | both | The producer cannot allocate a fresh HLC logical counter in the current millisecond without violating monotonicity. |
http_signature_invalid | code | 401 | endpoint | A per-delivery RFC 9421 HTTP Message Signature failed verification; the Content-Digest header profile, exact-content digest, or canonical-JSON wire check failed; or source_id disagreed with the Source-Service-ID header / signature transcript. Applies to Applet transaction push and to MIMI provider-to-provider writes (zh/extensions/mimi-interop.md §5). See zh/extensions/applet-integration.md §7.3.1. |
http_signature_required | code | 401 | endpoint | A service-to-service request that MUST carry a per-delivery RFC 9421 HTTP Message Signature presented only Authorization: Bearer with no Signature. Applies to Applet transaction push in both directions (node->Applet and app/bridge->arkret edge inbound) and to MIMI provider-to-provider writes (zh/extensions/mimi-interop.md §5). See zh/extensions/applet-integration.md §7.3.1. |
human_approval_required | reason_code | — | auth_decision, service_call | An agent runtime requested a high-risk session scope that requires out-of-band controller approval. The top-level service error is claim_required; error.details carries this reason_code and an opaque approval_request_id. The runtime MUST NOT receive a CAPTCHA, OTP, or browser challenge. See zh/identity/key-management.md §3.2. |
ice_config_denied | code | 403 | service_call | Service refused to issue ICE configuration for this call or participant. |
identity_creation_already_accepted | reason_code | — | identity_creation, service_call | The exact Principal Control Realm genesis for this provisional identity has already been accepted and MUST NOT be abandoned. The Account Authority checks the frozen identity creation operation and durable PCR submission/result evidence. An uncertain dispatch is not evidence of non-acceptance. Return the stable terminal result without creating an orphan anchor tombstone, suppressing the checkpoint or releasing the lease. Established identities use the account deletion / erasure path. See zh/identity/key-management.md §5.0.2. |
identity_creation_challenge_already_consumed | reason_code | — | identity_creation, service_call | The identity-binding challenge was already consumed and the request is not a byte-identical replay whose canonical request digest matches the stored successful outcome. The Account Authority MUST reject it before any state transition; an exact replay returns the recorded outcome instead of this code. |
identity_creation_challenge_expired | reason_code | — | identity_creation, service_call | The persisted identity-binding challenge expired before first successful consumption. The Account Authority MUST reject before publishing the DID operation or relaying PCR genesis, and the client must obtain a fresh challenge for the same frozen draft. |
identity_creation_lease_fenced | reason_code | — | identity_creation | The identity-creation lease fence is no longer current. A stale holder cannot publish or complete the frozen account/principal registration. |
identity_method_evidence_invalid | reason_code | — | identity_resolution, auth_decision | Submitted method_history_evidence fails independent verification: broken inception/current hash chain, SCID mismatch, invalid controller proof, unmet witness threshold, or stale evidence. An Applet-managed principal MUST carry a complete webvh_log; did:web snapshots, did:key expansion and service attestation are not substitutes. See zh/extensions/applet-integration.md. |
illegal | reason_code | — | moderation_report | Standard moderation reason: content alleged to violate applicable law (CSAM, threats, IP infringement, etc.). |
initial_session_request_mismatch | reason_code | — | identity_creation | The InitialSessionGrantRequest digest, device id, audience, scope ceiling, or RFC 7638 thumbprint does not match the frozen registration and handoff holder binding. |
integrity_failed | reason_code | — | event_envelope, service_call | A recording or transcription artifact failed digest, encryption-context, or integrity verification. |
internal_error | code | 500 | endpoint | An internal service error occurred. |
invalid_ack_token | reason_code | — | service_call | A to-device message ack carried an ack token that does not correspond to a delivered to-device cursor (unknown, malformed, or already-superseded). Carried under param_invalid. See zh/sync/client-sync.md §10.1 and zh/sync/service-http-binding.md device_messages/ack. |
invalid_canonical_json | reason_code | — | encoding | Bytes are not valid Arkret canonical JSON (sorted keys, integer-only numbers, escape rules per encoding.md §1). |
invalid_cursor | reason_code | — | client_sync, encoding | Cursor payload fails the §8.2 / §8.3 cursor syntax or schema before integrity verification. HTTP endpoints surface this as top-level `param_invalid` with reason_code `invalid_cursor`. Expiry uses `cursor_expired`; handle lookup or cross-binding failures use `cursor_integrity_invalid`; cross-service portability misses use `cursor_unrecognized`. |
invalid_encoding | reason_code | — | encoding | Generic encoding violation (HLC format, UUIDv7 format, base64url alphabet, etc.) not otherwise classified. |
invalid_membership_transition | reason_code | — | authz, state_resolution | Realm or Circle membership admission rejects an illegal FSM edge with failed_precondition and zero writes; see models/common-fields.md section 4.5. |
invalidated_by_rate_limit | reason_code | — | auth_decision | An out-of-band invite code attempt was invalidated because the per-code attempt rate limit was exceeded. See zh/conformance/conformance-vectors.md §3.11 (ak.vector.invite.oob_code_entropy.v1). |
invite_already_terminal | reason_code | — | event_envelope, auth_decision | An Invite state transition is rejected because the target Invite is already in a terminal flow state (`accepted` / `rejected` / `revoked` / `revoked_by_capability_loss` / `revoked_by_inviter_left` / `expired` / `invalidated_by_rate_limit`). Note the Invite `state` is the invite flow axis (not the generic object lifecycle axis); see zh/models/governance-objects.md §5.3. |
invite_directed_invitee_mismatch | reason_code | — | event_envelope, state_resolution | The invitee account carried by an ak.invite.accept / cancel / revoke payload and the invite_directed_invitee record stored for that InviteId are not both absent and not both present and byte-equal. It closes both directions of zh/models/governance-objects.md section 5.3: a third-party Invite cannot release another account's directed slot with a forged invitee, and a directed Invite cannot hold its slot forever by omitting the field. |
invite_event_actor_mismatch | reason_code | — | service_call | Sub-reason for failed_precondition when ak.self.invites.command.dispatch.v1 resolves invite_event_id to an accepted Event whose signing actor is not the authenticated session actor. The request carries only the Event ID; the service MUST NOT co-sign or re-author the resolved Event; see zh/sync/invite-addressing.md §7. |
invite_event_unaccepted | reason_code | — | service_call | Sub-reason for failed_precondition when ak.self.invites.command.dispatch.v1 supplies an invite_event_id that this Station cannot resolve to an accepted durable ak.invite.create Event. Private delivery only starts from that resolved accepted Event; see zh/sync/invite-addressing.md §7. |
invite_kind_requires_revoke | reason_code | — | event_envelope, auth_decision | ak.invite.cancel targeted a token/3PID Invite without a stored direct invitee binding. Only ak.invite.revoke may terminate that Invite class. |
invite_live_target_occupied | reason_code | — | event_envelope, auth_decision | Sub-reason for failed_precondition when ak.invite.create targets an account whose Realm live-target slot invite_live_target is already claimed by another live directed Invite. The Event is not accepted, enters no canonical history and derives no typed current result write; the closed error.details is InviteLiveTargetOccupiedProblem carrying the occupying invite_id and create_event_id. See zh/models/governance-objects.md section 5.3. |
invite_oob_entropy_too_low | reason_code | — | auth_decision | An out-of-band invite code was rejected because its entropy is below the required floor. See zh/conformance/conformance-vectors.md §3.11 (ak.vector.invite.oob_code_entropy.v1). |
join_policy_duplicate_gate_id | reason_code | — | schema_validation, state_resolution | Sub-reason for a schema_violation on ak.realm.join_policy where gates[] contains duplicate gate_id values. gate_id MUST be stable and unique within the policy so that audit refs in ak.member.state{gate_proofs[gate_id=…]} are unambiguous. Wire response uses code=schema_violation with reason_code=join_policy_duplicate_gate_id. See zh/governance/join-policy.md §3.1. |
join_rule_policy_mismatch | reason_code | — | event_envelope, auth_decision | Realm `ak.realm.join_rule` and `ak.realm.policy_bundle` declare conflicting join modes (e.g., `restricted` with no gate configuration, or `knock_restricted` with all-auto gates degrading to `restricted`). See zh/governance/join-policy.md §2. |
json_invalid | code | 400 | both | JSON body cannot be parsed. |
key_backup_wire_schema_required | reason_code | — | schema_validation | Station device/key surface received a `ak.keys.backups.*` request body that does not validate as `ak.schema.key_backup.v1`. Wire backups MUST carry the dedicated key-backup envelope with `series_id` and `series_seq`; client-local secret-storage envelopes are not accepted on wire endpoints. See zh/crypto-media/device-lifecycle.md §11. |
key_replay | code | 409 | endpoint | A key upload, claim, consume, or device-message operation reuses one-time key material or a claim nonce outside the allowed single-use window. |
key_transparency_proof_missing | code | 422 | both | A profile requiring log-backed key transparency omitted its inclusion proof, consistency proof, log head, or required witness evidence. |
key_unavailable | code | 409 | both | Required encrypted content key, MLS epoch, or authorized key share is not currently available. |
keypackage_already_consumed | code | 409 | endpoint | An MLS KeyPackage claim/consume/revoke operation targeted a KeyPackage that has already been consumed by a Welcome. |
keypackage_claim_rate_limited | reason_code | — | service_call | Internal server-side audit reason recorded when an MLS KeyPackage claim exceeds the per-(requester_id, target_principal_id) rate limit. The outward response MUST stay anti-enumeration (generic `claim_failed` or rate-limited envelope) and MUST NOT leak target existence; this reason is the canonical audit-log token only. See zh/conformance/scalability-constraints.md §6 and zh/identity/key-management.md. |
keypackage_expired | reason_code | — | keypackage_lifecycle | A reusable last-resort KeyPackage was revoked because its expires_at deadline elapsed. It MUST NOT be returned by a later claim. This is a revocation reason, not a KeyPackage lifecycle state. See zh/crypto-media/encryption-and-audit.md §2.6.2. |
keypackage_rotated | reason_code | — | keypackage_lifecycle | A reusable last-resort KeyPackage was revoked because its holder came online and rotated it to fresh init / encryption key material. This is a revocation reason, not a KeyPackage lifecycle state. See zh/crypto-media/encryption-and-audit.md §2.6.2. |
keypackage_unknown | code | 404 | endpoint | The referenced MLS KeyPackage is unknown, expired, revoked, or not visible to the caller under the active claim policy. |
keypackage_welcome_envelope_mismatch | reason_code | — | event_envelope, auth_decision | An MLS Welcome arrived with a `claim_envelope` whose canonical signing input does not match the Welcome's actual intended_realm_id / claim_id / requester_actor_id, or the envelope signature does not chain to the requester's PCR current accepted device signing key and authorization Event. See zh/crypto-media/encryption-and-audit.md §2.6. |
last_resort_not_supported | reason_code | — | service_call, feature_discovery | A KeyPackage claim requested a last-resort fallback from a server that does not advertise `ak.feature.mls_last_resort_keypackage.v1`. The server MUST continue to fail closed on an empty single-use pool and MUST NOT return a `last_resort=true` package. See zh/crypto-media/encryption-and-audit.md §2.6.2. |
last_resort_realm_affinity_violation | reason_code | — | service_call, auth_decision | An attempt to reuse a last-resort KeyPackage outside its `intended_realm_id` Realm-scoped last-resort pool. Servers MUST reject. See zh/crypto-media/encryption-and-audit.md §2.6.2. |
last_resort_rotation_required | reason_code | — | service_call, crypto | A holder that joined groups via a last-resort KeyPackage came online but has not rotated the package and performed the required group self-updates. Those updates do not retroactively restore old Welcome confidentiality. See zh/crypto-media/encryption-and-audit.md §2.6.2. |
legal_hold_active | reason_code | — | auth_decision | Requested operation targets a blob / object currently under legal hold. ak.self.blob.command.presign.v1 / ak.blob.delete / redaction-equivalent operations MUST be rejected with this code; legal hold takes precedence over capability and TTL. See zh/crypto-media/media-and-blob.md §5.4.4.1. |
limit_exceeded | code | 413 | both | A semantic protocol limit was exceeded even though the request body itself may be well-formed, such as a bounded recurrence expansion that cannot be returned within the v1 maximum result count. |
media_negotiation_failed | code | 409 | service_call | SDP / ICE media negotiation failed after the request passed authorization and schema validation. Clients MAY retry with a fresh offer or rejoin flow; services MUST NOT treat this as authorization success for any durable call-state transition. |
media_negotiation_timeout | reason_code | — | event_envelope, state_resolution | Call connection or capture media negotiation exceeded the registered timeout. |
media_permission_denied | code | 403 | service_call | Caller lacks permission to create, join, answer, or modify the media session. |
media_plaintext_service_not_authorised | reason_code | — | auth_decision | An SFU / MCU attempted to negotiate plaintext-decrypting media role without a matching Realm policy plaintext_visible_services[] entry whose data_classes[] contains media_plaintext, OR without the active media key-access revision covering media_service_decrypts=true. Free-text purposes do not grant authority. MUST be rejected; the SFU may still act as opaque RTP relay. See zh/crypto-media/media-service-binding.md §8.2. |
media_plaintext_warning_required | reason_code | — | auth_decision | A client attempted to join a call where media_service_decrypts=true without first displaying the required prominent plaintext-service warning and obtaining explicit second confirmation. The client MUST reject the join before releasing a token or media key. See zh/crypto-media/media-service-binding.md §8.2. |
media_service_binding_uncovered | reason_code | — | service_call, state_resolution | A media token / focus join was presented but the current MLS epoch governance binding does not cover the `ak.realm.media_service` binding the token relies on (token issuer trust-root sealing is unverifiable). The verifier MUST fail closed instead of trusting an uncovered media binding. See zh/crypto-media/media-service-binding.md §2.1. |
media_service_foci_required | reason_code | — | service_call, schema_validation | `ak.realm.media_service` is missing the required non-empty `foci[]` list. Services MUST reject payloads that do not declare explicit media foci and MUST NOT infer a focus from unrelated endpoint fields. See zh/crypto-media/media-service-binding.md §2. |
media_source_unavailable | reason_code | — | event_envelope, service_call | The source media required for recording or transcription was unavailable. |
member_identity_proof_invalid | reason_code | — | event_envelope, state_resolution | The MemberIdentity object carried by ak.member.identity.update failed proof validation: proof.payload_digest does not equal the sha256 of the proof-less MemberIdentity RFC 8785 JCS canonical bytes, the signature does not verify under proof.verification_method, or the method is not controlled by the disclosed subject_actor_id. The event MUST NOT be promoted to a verified display identity. See zh/sync/client-sync.md §8.1. |
member_identity_replacement_digest_mismatch | reason_code | — | event_envelope, state_resolution | A ak.member.identity.update replaces[] entry references an event whose payload.identity_payload carrier digest does not equal the declared payload_digest, or references an event under a different (realm_id, member_id, segment). The replacement edge is invalid; receivers MUST NOT remove the referenced event from the effective set on its basis. See zh/sync/client-sync.md §8.1. |
member_identity_state_mismatch | reason_code | — | event_envelope, state_resolution | The optional expected_state_digest optimistic-concurrency guard on ak.member.identity.update does not equal the digest of the current effective set for the same (realm_id, member_id, segment): sha256 over RFC 8785 JCS of the exact signed payload objects of the effective updates ordered by event_id ascending, [] when empty (zh/sync/current-results.md section 2). The Station MUST reject the Event with failed_precondition and zero writes instead of applying it as a valid replacement. |
member_identity_unknown_segment | reason_code | — | event_envelope, schema_validation | A ak.member.identity.update declared a segment value outside the v1 core enum (member_identity). Receivers MUST reject unknown segment values until a schema / profile revision extends the enum. See zh/sync/client-sync.md §8.1. |
membership_compensation_conflict | code | 409 | endpoint | A compensation delegation/action/executor/admission/join binding is wrong, already consumed, absent, or conflicts with the current membership provenance. The operation performs no write on conflict and never removes a newer join Event. |
method_not_allowed | code | 405 | endpoint | The path exists but the HTTP method is not supported. |
mimi_draft_unsupported | reason_code | — | service_call, schema_validation | Counterparty declared a MIMI Internet-Draft version not supported by this interop profile. Facade MUST reject instead of guessing a nearby draft shape. See zh/extensions/mimi-interop.md §4.1. |
mimi_e2ee_boundary_unmarked | code | 400 | endpoint | The MIMI payload crosses the E2EE boundary without being marked. |
mimi_governance_binding_mismatch | reason_code | — | service_call, state_resolution | A MIMI facade found a governance binding, but its authenticated GroupInfo/GroupContext group id, room-binding group id, group id derived from current accepted MlsGroupCurrent.effective_scope, fixed binding fields, provider DID or target Realm/Strand do not agree. Receiver MUST quarantine or reject with zero projection writes. See zh/extensions/mimi-interop.md §4.1. |
mimi_governance_binding_missing | reason_code | — | service_call, state_resolution | A MIMI facade attempted to project room state, groupInfo, key material, or message data into a Arkret Realm without a verifiable Arkret MLS governance binding. Receiver MUST quarantine or reject fail-closed instead of accepting unauthenticated MIMI state as Realm authority. See zh/extensions/mimi-interop.md §4. |
mimi_observer_write_forbidden | reason_code | — | service_call, authz | The current accepted MIMI room binding has local_provider_role=observer during ak.open.mimi.command.submit_message.v1. The registered observer_role_guard rejects the whole request before Event construction, persistence, receipt or fanout, with zero write effect. Producer path: contract-registry.json operation_registry admission_guards. See zh/extensions/mimi-interop.md §7. |
mimi_payload_digest_mismatch | code | 400 | endpoint | The MIMI payload digest does not match. |
mimi_payload_invalid | code | 400 | endpoint | The MIMI payload is invalid. |
mimi_policy_revision_mismatch | reason_code | — | service_call, state_resolution | A MIMI room policy component does not match the accepted Arkret ak.realm.policy_bundle revision. Facade MUST reject the update until a fresh policy projection is available. See zh/extensions/mimi-interop.md §4.1. |
mimi_provider_unreachable | reason_code | — | service_call | Required MIMI provider directory, key material, or groupInfo dependency is temporarily unreachable. Facade MAY retry with bounded backoff but MUST NOT accept fallback state without governance binding. See zh/extensions/mimi-interop.md §4.1. |
mimi_reporter_resolution_required | code | 403 | endpoint | MIMI reporter resolution is required before this action. |
mimi_room_binding_event_invalid | code | 400 | endpoint | The caller-authored room binding Event is missing, unexpected, semantically inconsistent, or does not exactly bind the authenticated MIMI room update. These pre-admission causes deliberately share one outward envelope; the precise reason is audit-only. |
mimi_room_binding_migration_proof_invalid | reason_code | — | state_resolution | A structurally valid ak.mimi.room_binding Event carries migration fields on another transition, its committed Event/RealmCommit lineage is not the same-room adjacent accepted->migrating current source, its asserted topology conflicts with the selected outcome, or its target MLS group conflicts with current accepted authenticated public state. Authority Event admission MUST reject with zero durable effects; missing or unpaired fields fail schema_violation first. See zh/extensions/mimi-interop.md §4.2. |
mimi_room_binding_status_transition_invalid | reason_code | — | state_resolution | A ak.mimi.room_binding Event declared a payload.status value that is not a legal transition from the binding's current status (including an illegal initial status or any write after the terminal revoked state). Reducer MUST reject; the room binding status lifecycle is defined in zh/extensions/mimi-interop.md §4.2. |
mimi_room_state_incompatible | reason_code | — | service_call, schema_validation | Incoming MIMI room state uses lifecycle, membership, policy, or extension shape not supported by the declared Arkret MIMI interop profile. Facade MUST reject or require a newer profile. See zh/extensions/mimi-interop.md §4.1. |
minimal_disclosure_violation | reason_code | — | moderation_decision, moderation_report | A moderation evidence package discloses material beyond the minimal-disclosure obligation (for example unrelated plaintext message bodies or MLS private state). The evidence submission MUST reject. See governance/content-moderation.md. |
misinformation | reason_code | — | moderation_report | Standard moderation reason: misleading / false information posing harm. |
mls_activation_irreversible | reason_code | — | state_resolution | Sub-reason for failed_precondition when an Event would deactivate, replace or re-run the accepted ak.mls.genesis of an already activated Realm, Circle or Sidecar scope. Activation is a one-way transition with no protocol path back to plaintext. See zh/models/realm-and-space.md section 2.3. |
mls_activation_required | reason_code | — | state_resolution | Sub-reason for failed_precondition when a Strand / Message / Morph / Blob content or metadata write carries plaintext into a scope whose ak.mls.genesis is already accepted. An activated scope accepts only RFC 9420 application ciphertext. See zh/models/circle.md section 7. |
mls_genesis_already_exists | code | 409 | both | An MLS genesis operation attempted to initialize a group whose genesis state is already durably accepted. Receivers MUST preserve the existing group state and reject the conflicting initialization. |
mls_genesis_binding_proposal_mismatch | code | 409 | endpoint | The proposed_group_genesis_binding differs from the signed Genesis binding, from the winning concurrently accepted Genesis binding, or is supplied after an accepted Genesis already exists. The losing caller MUST discard the proposal-bound proof, re-query without a proposal against the winning immutable binding, and MUST NOT reuse the old query/cache entry. |
mls_governance_anchor_unreachable | code | 409 | endpoint | The exact canonical proof_target_basis does not dominate the exact caller-supplied, locally trusted canonical proof_base_basis: at least one base leaf is neither retained as a target leaf nor an ancestor of a target leaf. Concurrent or otherwise unreachable cuts produce this error; byte-identical base and target antichains are a valid zero-transition query and MUST NOT produce it. Missing required RealmCommit/Event/witness material that prevents the service from deciding dominance is revision_unavailable instead. The service MUST NOT substitute a single head, common descendant, target, or untrusted base. The caller MAY retry only with another complete locally trusted basis or another authorized proof service. |
mls_governance_binding_stale | reason_code | — | state_resolution, auth_decision | A decrypting media service join or token request found that the current MLS group has not yet covered its current key_access_revision (a membership change is still awaiting its winning ak.mls.commit). Media policy fields such as media_service_decrypts and plaintext_visible_services are authorized from the current accepted policy projection and never advance key_access_revision. Receivers MUST refuse to act until a Commit covers the current revision. See zh/crypto-media/media-service-binding.md §8.2 and zh/crypto-media/encryption-and-audit.md §2.4.1. |
mls_keypackage_claim_request_expired | code | 409 | endpoint | The MLS KeyPackage claim request has expired. |
moderation_control_split | reason_code | — | policy_decision, state_resolution | Committed moderation control evidence is internally inconsistent or cannot be verified; downstream decisions depending on it fail closed until a complete valid state is available. |
moderation_state_conflict | reason_code | — | auth_decision, state_resolution | The referenced moderation state cannot be verified as a complete valid projection. Read, write, and distribute paths MUST fail closed. See zh/governance/content-moderation.md. |
morph_already_terminal | reason_code | — | event_envelope, auth_decision | `ak.redaction` targeting a Morph is rejected because the target Morph is already in terminal state `redacted`. |
morph_kind_immutable | code | 422 | both | A Morph update attempted to change an immutable morph_kind after creation. |
morph_not_active | reason_code | — | event_envelope, auth_decision | `ak.morph.archive` / `ak.morph.update` rejected because the target Morph is not in `active` state. |
morph_not_archived | reason_code | — | event_envelope, auth_decision | `ak.morph.restore` rejected because the target Morph is not in `archived` state. |
morph_profile_widens_schema_ref | code | 422 | both | Morph profile attempted to widen or replace schema_ref in a way that violates create-locked morph type rules. |
no_strand_track_message_grant | reason_code | — | auth_decision | No active capability grant authorises ak.message.create on the targeted Strand track for the actor. |
not_found | code | 404 | both | The target does not exist or is not visible to the requester. |
not_implemented | code | 501 | both | The operation is registered but this deployment has not implemented the endpoint or binding. |
not_member | code | 403 | both | Actor is not a member of the target Realm or scoped membership set for this operation. |
nsfw | reason_code | — | moderation_report | Standard moderation reason: not-safe-for-work / explicit adult content posted outside permitted contexts. |
object_id_not_event_derived | reason_code | — | event_envelope | Sub-reason for schema_violation when a create Event carries an object identifier in its payload for an object kind whose id MUST be derived from the create Event's event_id. Create payloads MUST omit the id; the reducer materialises it by retyping the complete event-derived EventId token. See zh/models/common-fields.md. |
ok | reason_code | — | batch_item, auth_decision, policy_decision | Sentinel value indicating an item or decision succeeded with no further reason. |
one_time_keys_exhausted | code | 409 | endpoint | No suitable one-time key or KeyPackage remains available for the requested device, principal, cipher suite, or profile. |
operation_selector_required | code | 400 | both | Every canonical Arkret HTTP or TUS request MUST carry exactly one Arkret-Operation selector naming the exact locally advertised operation_id before body parsing, including endpoint families with one candidate. A missing selector fails with operation_selector_required. Endpoint uniqueness, payload shape, SDK version, defaults, and fallback MUST NOT replace the selector. WebSocket open frames carry the operation_id required by their frame schema. |
operator_rejected | reason_code | — | device_recovery | Recovery-session `rejection_reason_code` value: an operator / admin surface explicitly rejected the session. Closed value set defined in artifacts/schemas/recovery-session.schema.json; completion ownership is defined in zh/identity/security-transactions.md §2. |
organization_registration_challenge_invalid | code | 400 | endpoint | The referenced organization registration challenge is unknown, expired, already consumed without an exact successful-replay ledger match, or its purpose / audience / origin / trust_domain / local_admin_subject / requested_scopes binding does not match the submitted request. On first success the registry atomically records (challenge_id, canonical_request_digest, outcome): a byte-identical retry returns that outcome, while the same challenge with a different digest fails here rather than authorising a second intent. |
organization_registration_control_proof_invalid | code | 400 | endpoint | The method-native control proof fails verification, was produced under a different signing context, does not bind the submitted challenge / organization / version_id, or its signer is not in the organization DID's control relationship at the pinned version. Resolvability of the DID is never accepted in place of this proof. |
organization_registration_quorum_not_met | code | 400 | endpoint | proof_kind=governance_quorum but fewer than quorum_threshold distinct valid governance signatures were supplied, or two signatures resolve to the same verification method. JSON Schema cannot compare the array length against the declared threshold, so the receiver enforces it and fails closed rather than accepting a partial quorum. |
organization_registration_revoked | code | 409 | endpoint | The local organization binding generation is revoked, which is terminal. This also covers an older generation atomically terminated with reason organization_registration_superseded when a new current generation was opened. Refresh MUST NOT revive it, and a receipt from it MUST NOT authorize after the current-generation pointer advances: a binding withdrawn locally, superseded by changed admin/scopes, or forced to revoked because the external DID was deactivated can only be replaced by a fresh registration with a new challenge and proof. |
organization_registration_stale | code | 409 | endpoint | The organization binding is stale — the pinned version no longer reflects current control after a controller rotation, or the receipt has passed expires_at — and the attempted operation is on a high-risk path. Low-risk reads may still proceed; high-risk paths MUST fail closed until a successful refresh, so that one first-time proof cannot authorise the relationship indefinitely. |
other | reason_code | — | moderation_report | Standard moderation reason: catch-all for reports that do not fit the named categories. MUST be accompanied by a free-text `description` field. See zh/governance/content-moderation.md §3.2. |
overbroad_request | code | 422 | service_call | A presentation request asks for unrelated handles, credential identifiers, or global identifiers beyond its declared purpose. |
pairing_expired | reason_code | — | auth_decision, event_envelope | Agent bootstrap pairing window elapsed before the first runtime key authorization completed. Generic list/get views close the open handle and report readiness not_ready with runtime_key_missing; only the pairing poll may return its operation-local runtime_state=pairing_expired diagnostic. Pairing expiry does not create, revoke or rewrite Realm grants. It never applies to previously keyed Agents: an expired replacement handle only clears open fields and pairing_open readiness. |
pairing_request_expired | reason_code | — | auth_decision, service_call | A `ak.gate.account.command.pair_agent_key.v1` pairing request was presented after its `pairing_expires_at` (or the runtime key-pairing session id has been closed). The endpoint MUST fail closed; the controller MUST initiate a fresh pairing strand. See zh/identity/key-management.md §3.6.2. |
param_invalid | code | 400 | both | A parameter value is syntactically or semantically invalid. |
param_missing | code | 400 | both | A required parameter is missing. |
partial_auth_state | reason_code | — | state_resolution | Authorization state for the event could not be fully resolved from the material the evaluator holds: a required grant, delegation link or current revocation status was not retrievable. It is an incomplete-evaluation diagnostic, never an admission outcome. The evaluator MUST NOT derive an allow cache entry from it and MUST either retry the dependency fetch or fail closed; a resolvable dependency that is merely absent is dependency_missing. See zh/authz/capabilities.md §10.3. |
participant_binding_invalid | reason_code | — | event_envelope, service_call | A `ak.call.state` participant's `participant_binding` failed one of: (a) issuer_kid resolution against current `ak.realm.media_service.service_id`; (b) field consistency with the participant entry (`realm_id` / `call_id` / `focus_id` / `actor_id` / `device_id` / `participant_id`); (c) `expires_at` freshness vs event `created_at`; (d) signature verification. Reducer MUST `failed_precondition`. See zh/crypto-media/call-state.md §4.1. |
participant_id_unrecognised | reason_code | — | service_call | Backend (LiveKit / SFU / etc.) signalled `ParticipantConnected` with a `participant_id` that has no matching value in the accepted call roster effective authority-ordered keyed set (or matches a value whose `participant_binding` fails verification). Client MUST refuse to establish media streams for that participant — this closes the attack where a compromised backend tries to inject unauthorized participants into the conference. See zh/crypto-media/media-service-binding.md §7. |
patch_atomic_conflict | reason_code | — | event_envelope, state_resolution | A single payload.patch contains parent/child writes, duplicate target paths, selector-affecting writes, or another multi-path combination that cannot be applied as one deterministic atomic Event. Reducer MUST reject the whole patch rather than partially applying paths. See zh/models/event-and-patch.md §4.3.1. |
patch_path_invalid | reason_code | — | event_envelope | An `ak.schema.patch.v1` patch path violates the ABNF grammar in zh/models/event-and-patch.md §4.2.1 (malformed identifier, quoted identifier, selector or numeric-index form, path > 1024 bytes, or nesting > 16 segments). Parser MUST NOT attempt fallback recovery; reducer rejects with this reason. |
patch_path_reducer_managed | reason_code | — | event_envelope | An `ak.schema.patch.v1` patch path addresses a field the generic update surface does not own. The normative per-object path set is registry/reducer-managed-path-registry.json (universal minimum set plus per-object-kind additions, minus the named View `state` exemption); the description here is not the criterion and MUST NOT be read as one. The forbidden path and every dotted descendant of it are rejected together. See zh/models/event-and-patch.md §4.2.5. |
patch_unset_redactable_field | reason_code | — | event_envelope | An `ak.schema.patch.v1` `$op="unset"` addressed a redactable content-carrier slot. The normative path set is registry/redactable-field-registry.json (Message / Morph content pairs; Strand Description and synthesis content pairs) plus any Realm-schema field marked `redactable: true`; the description here is not the criterion. Absence of a content slot on the materialized object is reserved for `never authored` and `cleared by redaction`, so an ordinary update MUST NOT remove it. This is a slot-existence rule, not a capability boundary: `$op="set"` on the same path is ordinary authoring and MUST be accepted even when the new value carries an empty body, and no patch op can reproduce the whole-object, terminal, audit-committed effect of redaction. `metadata`, `encrypted_metadata`, `metadata.title`, `metadata.summary` and paths under `metadata.fields` are NOT covered and MUST accept `$op="unset"`. See zh/models/event-and-patch.md §4.2.4. |
payload_digest_mismatch | code | 422 | both | Encrypted payload digest does not match the declared encrypted envelope content. |
payload_too_large | code | 413 | both | The request or blob exceeds declared size limits. |
pcr_authority_stale | code | 409 | both | The accepted PCR authority checkpoint, device generation, recovery-policy version or account-local-lineage binding is stale. DID freshness cannot repair this failure. |
pcr_genesis_conflict | reason_code | — | pcr_genesis | The deterministic Principal Control Realm already has a different authoritative genesis unit. The receiver MUST perform zero writes and MUST NOT replace the accepted founding device. |
pcr_genesis_unit_invalid | reason_code | — | pcr_genesis | The closed ordered pair is not exactly one root-signed ak.realm.create followed by one founding-device-signed ak.device.authorize, or atomic validation failed. No partial write is permitted. |
peer_state_stale_unavailable | code | 503 | service_call | The peer's state is stale and temporarily unavailable. |
permission_denied | reason_code | — | event_envelope, service_call | A recording or transcription backend could not obtain the required media permission. |
pin_target_not_pinned | reason_code | — | event_envelope, state_resolution | Sub-reason for failed_precondition when ak.pin.reorder addresses a (pin_scope, target_ref) that has no causally earlier surviving ak.pin.add assertion to inherit note and the remaining entry fields from. Reducers MUST NOT synthesize a rank-only entry that would put the target back into the roster. See zh/models/pins.md section 4. |
policy_combination_invalid | code | 422 | both | The submitted Realm policy combination (discoverability × join_rule × history_access) violates the Realm policy compatibility rules. Reducer keeps the prior accepted state. |
policy_denied | code | 403 | both | A Realm, organization, account, holder-disclosure, agent, or deployment policy explicitly denied the requested operation after syntactic validation and authentication succeeded. Use a narrower code when a more specific registry entry applies. |
policy_revision_gap | reason_code | — | event_envelope, state_resolution | A ak.realm.policy_bundle update skipped one or more monotonic policy_revision values. Reducer MUST reject instead of accepting a discontinuous realm_policy_bundle revision. |
policy_revision_rollback | code | 409 | service_call | A reducer rejected a Realm policy update because policy_revision is older than the current accepted revision. |
policy_revoked | reason_code | — | event_envelope, state_resolution | The Realm policy authorizing capture was revoked while the capture lifecycle was active. |
policy_stale | code | 409 | endpoint | The request depends on policy state older than the freshness window required for this operation or risk tier. |
policy_unavailable | code | 503 | endpoint | The policy service, Realm policy facet revision, or policy proof required for this operation is temporarily unavailable. |
policy_violation | code | 403 | both | A realm-level policy refuses the requested write or fanout (read-receipts §2.5: ak.receipt.read drops when disclosure='disabled'; retry_after_ms is null because retry will not change the outcome). |
presign_expired | reason_code | — | service_call, auth_decision | A ak.self.blob.command.presign.v1 bearer URL was presented outside its nbf / exp window or after its nonce was revoked. Wire response remains non-enumerating not_found where required; audit logs may record this reason. See zh/crypto-media/media-and-blob.md §5.4. |
presign_invalid | reason_code | — | service_call, auth_decision | A ak.self.blob.command.presign.v1 bearer envelope is syntactically invalid, has an unrecognised scheme, fails signature verification, mixes with Authorization header auth, or otherwise cannot be validated. Wire response remains non-enumerating not_found where required; audit logs may record this reason. See zh/crypto-media/media-and-blob.md §5.4. |
presign_scope_mismatch | reason_code | — | service_call, auth_decision | A ak.self.blob.command.presign.v1 envelope scope does not match the requested blob_ref, method, byte range, purpose, Realm, issuer trust state, or current blob visibility. Wire response remains non-enumerating not_found where required; audit logs may record this reason. See zh/crypto-media/media-and-blob.md §5.4. |
preview_policy_denied | code | 403 | service_call | A directory/resolve/search/projection request attempted to obtain a stripped preview, history stub, history snippet, or token-scoped preview that is not allowed by the effective ak.realm.preview_policy. External responses that must be non-enumerating MAY map this to not_found. See zh/governance/history-visibility.md §4. |
primary_track_required | reason_code | — | event_envelope, state_resolution | An ak.strand.tracks.update patch attempted to disable or remove the current primary track without atomically transferring primary status to another active track. Carried under failed_precondition; the patch is rejected atomically. See zh/models/strand-and-message.md section 4.7. |
principal_control_event_kind_forbidden | reason_code | — | event_envelope, auth_decision | A Realm claiming ak.profile.principal_control_realm.v1 received a collaboration / media / content event kind outside its allowlist-only control-plane event policy. Reducers MUST reject instead of treating the Realm as ordinary collaboration history. See zh/models/realm-and-space.md §2.8.1 and artifacts/profiles/conformance-profiles.json#profile_requirements. |
principal_deactivated | reason_code | — | auth_decision, event_envelope, service_call, state_resolution | The account status checkpoint contains a deactivation for the exact AccountId acting as actor, subject, issuer, recipient, or device owner. New device/session grants, KeyPackage operations, capability delegation, membership writes targeting that account, push routes, and to-device enqueue MUST fail closed. See zh/identity/account-lifecycle.md §7.1 and the federation propagation rules of zh/identity/account-lifecycle.md §7. |
principal_unknown | code | 404 | endpoint | The referenced principal DID or account principal is unknown or not visible to the caller. |
private_attachment | reason_code | — | service_call, auth_decision | The target blob is actor_private / private attachment material and MUST NOT be exposed through a bearer presign URL. It remains fetchable only through header-authenticated actor-bound access. See zh/crypto-media/media-and-blob.md §5.4.4.1. |
private_view_requires_account_data | reason_code | — | event_envelope, schema_validation | A shared Realm View Event attempted to persist visibility=private. Private Views are encrypted holder account data under ak.views.private.<view_id> and MUST NOT enter the shared reducer. Carried under schema_violation. See zh/models/views.md. |
profile_unavailable | reason_code | — | service_call | Per-actor outcome of ak.self.actor_profile.read.resolve.v1. Unknown actor, actor without an accepted global profile, actor that is not an effective joined member of the request realm_id, and a caller not authorized for that actor MUST all report this single value, so the only outward carrier for PCR-resident ak.profile.create / ak.profile.update cannot be used to probe membership or account existence. See zh/discovery/profiles-presence.md §2.3. |
projection_incomplete | code | 409 | endpoint | The requested projection cannot be claimed complete under the current supported feature set or dependency checkpoint. |
proof_binding_missing | reason_code | — | event_envelope, auth_decision | Proof lacks a required `domain` or `audience` binding on a cross-service, cross-trust-domain, federation, or multi-audience call. Receivers MUST fail closed rather than accept a single-audience proof across services. A profile MAY define a more specific reason. See zh/models/event-and-patch.md §3. |
proof_failed | reason_code | — | device_recovery | Recovery-session `rejection_reason_code` value: proof verification failures reached the server-side policy limit, so the session transitioned to `rejected`. Closed value set defined in artifacts/schemas/recovery-session.schema.json; completion ownership is defined in zh/identity/security-transactions.md §2. |
proof_invalid | reason_code | — | auth_decision, service_call | A runtime key-pairing or session-grant proof (DID `assertionMethod` signature, agent_key_proof transcript, etc.) failed signature verification, transcript binding, or `proof_kind` check. Distinct from `signature_invalid` in that the wire shape was syntactically valid but the proof semantics did not bind to the expected principal / nonce / audience. See zh/identity/key-management.md §3.6.2. |
push_gateway_unreachable | code | 503 | endpoint | The push gateway or downstream push provider could not be reached or is unavailable for this route. Dual-registered as a per-device reason_code for ak.edge.push.command.notify.v1 (see reason_codes[]). |
push_payload_too_large | code | 413 | endpoint | The notification payload exceeds the push profile, provider, or deployment size limit. Dual-registered as a per-device reason_code for ak.edge.push.command.notify.v1 (see reason_codes[]). |
push_route_limit_exceeded | reason_code | — | event_envelope, service_call | A `ak.device.push_route` registration would exceed the v1 wire limit of 16 active push_route entries per `(recipient_id, principal_id, device_id)`. The server MUST reject the new registration. See zh/crypto-media/device-lifecycle.md §5.6.2 and zh/conformance/scalability-constraints.md §6.1. |
push_route_registration_rate_limited | reason_code | — | service_call | Internal audit reason recorded when push-route registration / rotation writes for a `(recipient_id, principal_id, device_id)` exceed the default rate (8 writes per 60s). The outward response uses a generic rate-limited envelope; this reason is for server-side abuse detection only. See zh/crypto-media/device-lifecycle.md §5.6.2 and zh/conformance/scalability-constraints.md §6.1. |
push_target_unknown | code | 404 | endpoint | The requested push target, device route, or notification subscription is unknown or no longer visible. Dual-registered as a per-device reason_code for ak.edge.push.command.notify.v1 (see reason_codes[]). |
push_token_invalid | code | 400 | endpoint | The supplied push token is malformed, fails provider validation, or is not bound to the authenticated principal/device. Dual-registered as a per-device reason_code for ak.edge.push.command.notify.v1 (see reason_codes[]). |
push_token_unknown | code | 404 | endpoint | The push token or registration id is unknown, revoked, expired, or already unregistered. Dual-registered as a per-device reason_code for ak.edge.push.command.notify.v1 (see reason_codes[]). |
quarantine | code | 409 | both | The submitted item or operation result was quarantined by moderation, abuse, fork, policy, or risk handling and is not accepted as ordinary visible state. |
quarantined | reason_code | — | state_resolution, federation_transaction | Item was placed in quarantine pending operator decision (state_conflict_resolution); not finalized. |
query_invalid | code | 400 | both | Query parameters cannot be parsed or violate endpoint rules. |
queue_full | reason_code | — | batch_item | Per-event rejection reason in an Applet edge transaction response (rejected[].reason_code) when the receiving side's inbound processing queue is saturated (backpressure) -- the Applet for node-to-Applet pushes, the Arkret edge for Applet-to-node pushes. The push sender MAY re-deliver the rejected events later under the same idempotency identity. See zh/extensions/applet-integration.md §7.3. |
quorum_unreachable | reason_code | — | event_envelope, auth_decision | A threshold-governed control proposal can no longer collect the required independent acknowledgements before its deadline. The receiver MUST fail closed with reason_code=quorum_unreachable rather than guessing authority. |
quota_exceeded | code | 403 | both | Storage, bandwidth, or compute quota was exceeded. |
rank_exhausted | code | 409 | both | No valid fractional rank exists between the requested bounds; a rebalance or different position is required. |
rate_limited | code | 429 | both | The caller exceeded the current rate limit policy. Dual-registered as a per-device reason_code for ak.edge.push.command.notify.v1 (see reason_codes[]). |
reaction_scope_mismatch | reason_code | — | state_resolution | Sub-reason for failed_precondition when a ak.reaction.* target_ref resolves to an object outside the reaction event's stamped effective scope. Reactions MUST target an object within their own effective scope. See zh/models/strand-and-message.md §9.8.2. |
reaction_target_unsupported | reason_code | — | schema_validation, state_resolution | Sub-reason for schema_violation when a ak.reaction.add / ak.reaction.remove target_ref points at an object kind that the deployment does not allow reactions on. v1 core only allows ak:message: targets; profiles MAY register additional target kinds. See zh/models/strand-and-message.md §9.8.2. |
realm_alias_authority_mismatch | reason_code | — | state_resolution, service_call | An ak.realm.alias declaration carried an alias whose <domain> is not an authority domain of this Realm's trust_domain, so the RealmCommit signature from the governance Station is not evidence that the domain's alias issuer authorized the claim. Domain reducers and directories MUST fail closed instead of registering a foreign-domain alias. See zh/discovery/object-addressing.md §3.3. |
realm_alias_homograph_forbidden | reason_code | — | schema_validation, service_call | Realm alias registration collided with the same authority-local realm-alias-namespace UTS #39 skeleton index or failed its declared Highly Restrictive registration policy. Skeletons do not define canonical equality; cross-namespace handle/realm-alias homographs are disambiguated by sigil and type context. See zh/discovery/object-addressing.md §3.3. |
realm_alias_taken | reason_code | — | state_resolution, service_call | An ak.realm.alias declaration requested a canonical alias already held by a different Realm in the same issuing authority's realm-alias namespace. The alias registrar MUST reject the later claim rather than re-pointing the alias; releasing an alias requires the holding Realm to publish an ak.realm.alias tombstone first. Handle namespace occupancy is NOT a collision (the two namespaces are disjoint). See zh/discovery/object-addressing.md §3.3. |
realm_already_exists | reason_code | — | event_envelope, state_resolution | A ak.realm.create event attempted to create a Realm id whose genesis typed current result is already set. Reducer MUST reject the duplicate create without rewriting create-locked fields. |
realm_authority_controller_mismatch | reason_code | — | event_envelope, state_resolution, auth_decision | An Event presented ak:result:realm_authority_root:null as its authorization_ref but the typed current result's current controller_actor_id is not the authorizing ActorId, or the epoch / authority_generation bound at issuance no longer matches the typed current result in that basis. Includes replaying a staged genesis-batch root proof outside its atomic bootstrap unit. See zh/authz/capabilities.md section 3.2. |
realm_authority_root_conflict | reason_code | — | event_envelope, state_resolution | The authority-root typed current result value was author-supplied or otherwise diverges from the registered value_projection: controller_actor_id not equal to the create envelope actor_id, a non-zero controller_epoch or authority_generation at genesis, or members beyond the closed three-field shape. Reducer MUST reject the whole unit. See zh/models/realm-and-space.md section 2.5. |
realm_authority_root_missing | reason_code | — | event_envelope, state_resolution, auth_decision | The Realm has no registered realm_authority_root typed current result in the authorization basis, or an ak.realm.create bootstrap unit failed to materialize it. Reducer MUST reject the entire bootstrap unit atomically without leaving genesis, profile, policy, or membership facets, and MUST NOT fall back to membership or a realm_state.owner projection mirror. See zh/models/realm-and-space.md section 2.5. |
realm_federation_policy_closed | code | 403 | service_call | The realm federation policy is closed. |
realm_federation_policy_invalid | code | 403 | service_call | The realm federation policy is invalid. |
realm_federation_policy_quarantine | code | 403 | service_call | The realm federation policy has quarantined the peer. |
realm_federation_policy_restricted | code | 403 | service_call | The realm federation policy restricts this peer. |
realm_frozen | code | 403 | both | The target Realm is reversibly frozen or archived and the attempted write is outside the closed exemption set in zh/models/realm-and-space.md §2.6.0. A tombstoned Realm rejects ordinary writes with active failed_precondition; realm_terminal_state remains a reserved reason. |
realm_id_not_event_derived | reason_code | — | event_envelope | Sub-reason for schema_violation when ak.realm.create carries a forbidden envelope realm_id instead of the realm_genesis shape, when a Collaboration Realm does not retype the full 33-byte create Event token, when a Principal Control Realm does not match the 0x11 subject transcript, or when payload.object still carries an id field. See zh/models/realm-and-space.md section 2.5. |
realm_link_invalid_transition | reason_code | — | state_resolution | An ak.realm.link status transition is absent from the canonical domain-transition contract, including any non-byte-identical attempt to leave terminal tombstoned state. The reducer MUST reject with top-level failed_precondition. See zh/models/realm-links.md §4. |
realm_link_self_reference | reason_code | — | schema_validation, state_resolution | An ak.realm.link targets its own enclosing Realm. Self-links have no cross-boundary meaning and MUST be rejected with top-level schema_violation. General directed cycles remain valid. See zh/models/realm-links.md §2. |
realm_organization_authorization_invalid | reason_code | — | event_envelope, state_resolution | A ak.realm.organization statement failed organization-side authorization: payload.authorization.proof did not verify against the organization DID control state, threshold governance quorum, or the verification_method, or issuer_role is not allowed for the relationship. This is the generic organization-consent failure used when no more specific realm_organization_* reason applies. Downstream implementations MUST emit this exact code rather than inventing a bare string. |
realm_organization_delegation_missing | reason_code | — | event_envelope, state_resolution | A ak.realm.organization statement whose authorization.issuer_role is governance_service or account_authority omitted authorization.delegation_ref, or the referenced delegation does not resolve to a live organization DID delegation whose purpose covers ak.realm.organization and the requested relationship/control_scopes. |
realm_organization_expired | reason_code | — | event_envelope, state_resolution | A ak.realm.organization statement is outside its validity window: the evaluation time is before payload.not_before, after payload.expires_at, or outside the referenced delegation's validity period. Schema validation passes; the reducer MUST reject the statement as expired or not-yet-valid. |
realm_organization_realm_acceptance_missing | reason_code | — | event_envelope, state_resolution | A ak.realm.organization statement lacks the Realm-side acceptance layer: the writing principal does not hold ak.realm.admin and the event is not part of an allowed create-bootstrap initial-configuration batch. Realm-side acceptance is independent of the organization-side proof. |
realm_organization_scope_missing | reason_code | — | event_envelope, state_resolution | A ak.realm.organization statement asserts a control_scope (or relationship) that the verified organization-side authorization or its delegation does not cover. The endorsement boundary in payload.control_scopes exceeds what the proof/delegation grants. |
realm_state_snapshot_authority_unverified | code | 403 | endpoint | The snapshot issuer, witness quorum, or signing authority cannot be verified for the requested Realm and manifest time. |
realm_state_snapshot_chunk_digest_mismatch | code | 400 | endpoint | A conformance snapshot chunk digest does not match the declared digest. |
realm_state_snapshot_issuer_revoked | reason_code | — | client_sync, realm_state_snapshot_verification | Snapshot signer's authority (Realm owner / admin / trusted snapshot issuer / witness quorum membership) was revoked before the snapshot's origin admission/issuance gate, or the verifier cannot prove the frozen authority basis. Client MUST quarantine or reject the snapshot. A revoke accepted after a valid snapshot issuance does not retroactively invalidate that snapshot. |
realm_state_snapshot_unavailable | code | 503 | endpoint | The requested snapshot head or snapshot artifact is not currently available from this service. |
realm_terminal_state | reason_code | — | state_resolution, auth_decision | Reserved diagnostic for a terminal Realm. v1 tombstone admission rejects later ordinary writes with active failed_precondition without this reason; ak.realm.destroy production admission is unavailable. See zh/models/realm-and-space.md §2.6. |
realm_unavailable | reason_code | — | auth_decision, state_resolution | The effective target Realm is tombstoned, destroyed, unreachable, or not writable by the actor; default Realm resolution MUST fail closed instead of following Realm links or falling back implicitly. See zh/models/space-hierarchy.md §4. |
reauthentication_required | code | 401 | endpoint | A high-risk self-service action (for example ak.gate.account.command.request_erasure.v1) requires fresh high-risk action authentication — recent login, WebAuthn, recovery key or a deployment equivalent — and the presented session does not satisfy the deployment policy. The caller MUST re-authenticate and retry with new request material; the strength of the required proof is deployment governance. See zh/identity/account-lifecycle.md section 8.1 and section 10. |
recipient_unavailable | reason_code | — | service_call | A to-device delivery targeted a deactivated principal whose pending queue was dropped by the deactivation fanout; further delivery MUST fail closed rather than enqueue. See zh/identity/account-lifecycle.md §7.1. |
recording_artifact_pipeline_bypassed | reason_code | — | service_call | A backend (LiveKit Egress / Janus recording plugin / etc.) attempted to deliver a recording artifact outside the Arkret-side blob pipeline — e.g. an Egress destination pointing to LiveKit Cloud / S3 / GCS direct, instead of the Arkret media service authenticated upload endpoint. Clients MUST fail closed. See zh/crypto-media/call-state.md §5 and zh/crypto-media/media-service-binding.md §8.1. |
recording_consent_required | reason_code | — | service_call, event_envelope | A capture attempted to enter or stay in recording/transcribing without the consent evidence its Realm requires. The reachable trigger is the per-participant consent acknowledgment profile of zh/crypto-media/call-state.md §5.2: a recorded party has no current device-signed acknowledgment, or membership, device or capture epoch changed and the acknowledgment was not re-acquired. This code is NOT how a producer-declared consent value is refused: `call_recording_start_payload.result.retention.consent_confirmed` is `const: true`, so any other value is `schema_violation` before the reducer runs, and the capture-kind start-event ref is stamped by the reducer because the payload MUST NOT carry it. Reducer MUST reject before either capture FSM or result typed current result is written. |
recording_denied | code | 403 | service_call | Recording or transcription is denied by Realm policy or participant consent state. |
recording_state_transition_invalid | reason_code | — | event_envelope | An `ak.call.state` event requested a `recording_transition` or `transcript_transition` not listed in the per-capture controlled state machine (e.g. transitioning out of terminal `ready` / `failed`, `stopped → failed`, or attempting to enter a capturing state without a new `ak.call.recording.start`). The reducer MUST `failed_precondition`. The same code covers both orthogonal capture dimensions. See zh/crypto-media/call-state.md §4.2. |
recovery_authorization_device_mismatch | code | 409 | endpoint | The recovery authorization device does not match. |
recovery_authorization_principal_mismatch | code | 409 | endpoint | The recovery authorization principal does not match. |
recovery_authorization_session_mismatch | code | 409 | endpoint | The recovery authorization session does not match. |
recovery_control_event_kind_mismatch | code | 409 | endpoint | The recovery control event kind does not match the expected kind. |
recovery_control_event_not_found | code | 409 | endpoint | The referenced recovery control event was not found. |
recovery_evidence_unbound | reason_code | — | recovery_transaction | Recovery evidence does not bind the current transaction, recovery session, principal, replacement device or prepared-plan digest. |
recovery_list_update_device_mismatch | code | 409 | endpoint | The recovery list update device does not match. |
recovery_list_update_principal_mismatch | code | 409 | endpoint | The recovery list update principal does not match. |
recovery_policy_conflict | code | 409 | endpoint | The recovery policy conflicts with the current state. |
recovery_policy_device_unauthorized | code | 409 | endpoint | The device is not authorized under the recovery policy. |
recovery_policy_genesis_not_v1 | reason_code | — | schema_validation, state_resolution | A recovery policy publish is the first accepted policy for the principal but does not use `version=1`. Genesis recovery policies MUST start at version 1. |
recovery_policy_id_mismatch | code | 409 | endpoint | The recovery policy id does not match. |
recovery_policy_mismatch | code | 409 | both | A recovery session or proof does not satisfy the principal's declared recovery policy. Dual-registered as a service code and a reason_code (see reason_codes[]). See zh/identity/key-management.md §8. |
recovery_policy_missing | code | 409 | endpoint | No recovery policy is registered for the principal. |
recovery_policy_revoked | code | 409 | endpoint | The recovery policy has been revoked. |
recovery_policy_supersedes_invalid | reason_code | — | schema_validation, state_resolution | A non-genesis recovery policy rotation omits `supersedes` or names a predecessor other than the currently accepted policy_id for the principal. Servers MUST reject with 409 Conflict. |
recovery_policy_trust_domain_mismatch | code | 409 | endpoint | The recovery policy trust domain does not match. |
recovery_policy_version_mismatch | code | 409 | endpoint | The recovery policy version does not match. |
recovery_policy_version_not_monotonic | reason_code | — | schema_validation, state_resolution | A recovery policy publish or rotation uses a `version` that is not strictly greater than the currently accepted policy version for the principal. Servers MUST reject with 409 Conflict. |
recovery_principal_isolation | reason_code | — | authz, device_recovery | A recovery policy or recovery session request targets an AccountId different from the exact account bound to the authenticated SessionGrant. Servers MUST compare both principal_id and station_id and reject without revealing the target account's recovery state. |
recovery_proof_authority_invalid | code | 401 | endpoint | The recovery proof authority is invalid. |
recovery_proof_kind_not_allowed | code | 409 | endpoint | The recovery proof kind is not allowed. |
recovery_proof_kind_unimplemented | code | 501 | endpoint | The recovery proof kind is not implemented. |
recovery_proof_kind_unknown | reason_code | — | schema_validation, device_recovery | A recovery policy, receipt, or proof names a proof kind outside the ak.schema.recovery_policy.v1 methods kind union. Producers MUST use one of did_root, recovery_unlock, device_quorum, or trusted_recovery_service. |
recovery_receipt_completed_at_after_commit | reason_code | — | recovery_transaction | A RecoveryTerminalCommit carries a recovery_receipt whose completed_at is later than the Station's own linearized commit time for commit_recovery_unit. The receipt's completed_at is the replacement device's authoring time for "completed if every check passes", so it can never be later than the commit that would make it true. The Station MUST reject the whole submission with this deterministic reason and perform zero authoritative writes: no accepted step, no terminal result, no accepted Event, no committed RealmCommit, no generation advance, no activated device and no consumed recovery session. v1 defines no skew allowance, the Station MUST NOT sign a future-dated completion attestation and MUST NOT block waiting for the client clock. The rejected request was never accepted, so a corrected submission MUST use a new receipt; bytes already frozen as a step outcome still return duplicate_conflict. See zh/identity/security-transactions.md section 2.2. |
recovery_receipt_conflict | code | 409 | endpoint | The recovery receipt conflicts with the current state. |
recovery_required | reason_code | — | device_recovery, state_resolution | An existing account endpoint lacks the durable account MLS root, dependent snapshot/reference unit, or material claimed by its emitted marker. Ordinary feature APIs MUST stop and enter the existing recovery/pairing path; they MUST NOT mint a replacement root or tree. See zh/identity/key-management.md section 7.3.1. |
recovery_session_challenge_mismatch | reason_code | — | device_recovery, schema_validation | A recovery proof echoes a challenge value that does not exactly match the server-issued challenge for the referenced recovery_session_id. Servers MUST reject the proof before completing device recovery. See artifacts/schemas/recovery-session.schema.json and zh/identity/security-transactions.md §2. |
recovery_session_conflict | code | 409 | endpoint | The recovery session conflicts with the current state. |
recovery_session_id_reused | code | 409 | endpoint | The recovery session id has already been used. |
recovery_session_not_pending | code | 409 | endpoint | The recovery session is not in a pending state. |
reducer_projection_failed | reason_code | — | event_envelope, state_resolution | The reducer projection required by the registered contract cannot be derived uniquely from `kind`, signed envelope fields, schema-validated payload, and frozen pre-state. Receiver MUST reject the entire Event; projected writes are reducer output and never producer-selected Event fields. See zh/models/event-and-patch.md §4.3.1. |
refs_too_large | reason_code | — | schema_validation, event_envelope | Event Envelope refs[] exceeds the v1 maximum of 128 semantic refs. Receiver MUST reject with schema_violation. See zh/conformance/scalability-constraints.md. |
relation_already_terminal | reason_code | — | event_envelope, auth_decision | `ak.relation.tombstone` / `ak.relation.update` / equivalent Relation write rejected because the target Relation is already in the terminal state `tombstone`. In particular, `ak.relation.update` targeting a tombstoned Relation MUST be rejected with this reason_code. |
relation_kind_contains_derived | reason_code | — | event_envelope, schema_validation | Sub-reason for schema_violation when a direct ak.relation.create / update / delete targets a derived-projection contains shape (Space(board) -> Space(list) or Space(list) -> Strand). Truth sources are the space_parent and strand_position typed results written via ak.space.parent / ak.strand.move / ak.strand.reorder Events; only the non-derived object-composition contains form is directly writable. See zh/models/relation.md §3.2 and zh/models/realm-and-space.md §3.5-§3.6. |
relation_kind_watches_derived | reason_code | — | event_envelope, schema_validation | Sub-reason for schema_violation when a direct ak.relation.create / update / delete targets relation_kind=watches. The watches Relation is a derived projection only: its truth source is the strand_watch typed current result written via the ak.strand.watch.set durable event, never a direct Relation write. See zh/models/relation.md §3.2 and zh/models/strand-and-message.md §8. |
resolution_history_ancestor_unknown | reason_code | — | service_call | Sub-reason for param_invalid when ak.self.identity.read.resolution_audit.v1 receives an after_resolution_event_ref that is neither the genesis Event nor an accepted ak.identity.resolution.update in this account's current resolution lineage. The audit surface is already exact-current-holder authorized, so a stale or foreign cursor is reported as an invalid parameter rather than folded into the anti-enumeration outcome. See zh/identity/identity-did.md §4.2. |
response_invalid | code | 400 | service_call | A downstream service response was syntactically valid transport data but did not satisfy the expected protocol contract, including directory/projection/service-call schema mismatch or missing required pagination/error fields. |
revision_stale | code | 409 | both | The service's verified authority-stream head is behind the RealmCommit position required by the request. |
revision_unavailable | code | 503 | endpoint | The service cannot currently produce the requested checkpoint because required authority commit, reducer, or witness state is unavailable. |
revocation_freshness_unknown | reason_code | — | auth_decision, federation_transaction, state_resolution | Revocation / grant freshness cannot be established for a high-risk, cross-domain, or delegated action. Receiver MUST fail closed and return freshness diagnostics instead of treating missing revoke evidence as allow. |
risk_policy | reason_code | — | device_recovery | Recovery-session `rejection_reason_code` value: a server-side risk policy rejected the session. Closed value set defined in artifacts/schemas/recovery-session.schema.json; completion ownership is defined in zh/identity/security-transactions.md §2. |
routing_unlinkability_presign_forbidden | reason_code | — | authz, service_call | A presigned blob URL was requested for a Realm whose asset policy declares routing_unlinkability_required=true. The service MUST deny presign and require an authenticated proxy, OHTTP relay, or equivalent non-bearer direct download path. |
rsvp_basis_malformed | reason_code | — | event_envelope, schema_validation | entry.schedule_basis_refs does not carry exactly one well-formed ak:event: typed id: it is empty, holds more than one item, repeats an item, or the item is not a valid typed Event id. This shape admission is decidable from the payload field alone, without resolving the referenced Event, and MUST reject rather than pend. See zh/models/calendar-event.md section 8.1. |
rsvp_occurrence_not_canonical | reason_code | — | event_envelope, schema_validation | payload.occurrence is neither JSON null nor a canonical instance key (YYYY-MM-DD for all-day, YYYY-MM-DDTHH:MM:SS[Zone] for timed), including when its date component is not a real proleptic-Gregorian date. Receivers MUST reject instead of rewriting the key, since the typed current result subject derives from the signed value. See zh/models/calendar-event.md. |
runtime_key_missing | reason_code | — | agent_readiness | Closed generic Agent readiness blocker: no active accepted runtime key exists. It is durable subject-level readiness state and MUST NOT be inferred from a missing session or target-Realm grant. |
schema_violation | code | 422 | both | Parsed input does not satisfy the declared schema contract. |
scope_incomparable | reason_code | — | state_resolution | Sub-reason for failed_precondition when a structural relation, parent, position, cascade, or reverse-projected fact would need to span two sibling Circle scopes in the same Realm. v1 reducers MUST NOT choose either Circle, union them, or promote the fact to Realm-default. See zh/models/circle.md §6.1. |
scope_rebind_forbidden | reason_code | — | state_resolution | Sub-reason for failed_precondition when scope_circle_id rebind is attempted without an explicitly profile-permitted audited-high-risk path. Default reducer rejects rebinds to prevent silent historical-discussion migration. See zh/models/circle.md §6.1. |
scope_ref_mismatch | reason_code | — | event_envelope, auth_decision, state_resolution | The signed Event `scope_ref` does not equal the security scope deterministically resolved from the target or referenced accepted object state. Receiver MUST reject the Event and MUST NOT rewrite or reducer-stamp the signed scope. See zh/models/circle.md §6.1. |
scope_unavailable | reason_code | — | state_resolution | Sub-reason for failed_precondition when an object write references an effective scope whose Circle or parent Realm has been tombstoned/destroyed and cannot accept new writes. Projections may surface the same string as a non-error status marker. See zh/models/realm-and-space.md §2.6.1 and zh/models/circle.md §9.2. |
segment_aead_failed | reason_code | — | crypto, service_call | Streaming-chunked AEAD attachment: a per-segment AEAD tag fails to verify. Receivers MUST reject the segment and abort the stream. See zh/crypto-media/media-and-blob.md §3.3.6. |
segment_bounds_invalid | reason_code | — | schema_validation, service_call | Streaming-chunked AEAD attachment: a segment index is out of range, a segment length violates `segment_bytes`, or `segment_count` disagrees with the observed stream. Receivers MUST reject. See zh/crypto-media/media-and-blob.md §3.3. |
segment_replay | reason_code | — | crypto, service_call | Streaming-chunked AEAD attachment: a segment index appears more than once in the stream. Receivers MUST reject. See zh/crypto-media/media-and-blob.md §3.3.6. |
segment_sequence_invalid | reason_code | — | crypto, service_call | Streaming-chunked AEAD attachment (`ak.blob.stream_aead.v1`): segment indices arrive out of order, skip a value, or leave a gap. Receivers MUST reject. See zh/crypto-media/media-and-blob.md §3.3.6. |
segment_stream_truncated | reason_code | — | crypto, service_call | Streaming-chunked AEAD attachment: the stream ended without a valid final segment (last_segment_flag never observed, or fewer segments than `segment_count`). Receivers MUST reject to resist truncation. See zh/crypto-media/media-and-blob.md §3.3.6. |
selector_actor_wildcard_forbidden | reason_code | — | auth_decision, schema_validation | The resource selector attempted to use actor:*. v1 actor selectors MUST name a concrete DID; universal subject grants are not accepted. |
selector_governance_wildcard_forbidden | reason_code | — | authz, schema_violation | Governance-plane resource selector wildcard (e.g. policy:*, schema:*, or a governance object:* selector) was used without the required mitigation (denied by deployment policy, or constrained with max_authority_depth=0 plus bounded expiry plus admin approval). Receiver MUST reject. |
selector_missing_realm_scope | reason_code | — | authz, schema_validation | Sub-reason for schema_violation when a resource selector names a Realm-local kind without a realm_id, including the shorthand form where both the realm part and the object id are the wildcard. Parsers MUST reject the whole grant rather than silently treating the selector as global. See zh/authz/resource-selector-grammar.md sections 3.1 and 6. |
selector_too_complex | code | 422 | both | Resource selector or constraint exceeds parser hard limits defined in resource-selector-grammar.md §3.3 (string length, resources[] length, token count, nesting depth, single-field length, required_claims item count, constraint nesting). Dual-registered as a service code and a reason_code (see reason_codes[]) so it can be emitted as a top-level error and audit / abuse-detection can separate suspected parser-DoS attempts from ordinary format errors. See zh/authz/resource-selector-grammar.md §5. |
send_failed | reason_code | — | state_resolution | Invite delivery to the private target failed after the delivery service exhausted the retry budget. Used as a stable invite transition reason for ak.invite state projections; see zh/models/governance-objects.md and zh/sync/third-party-invites.md §6.1. |
series_chain_broken | reason_code | — | schema_validation, state_resolution, device_recovery | ak.schema.key_backup.v1 envelope chain failed verification: a `supersedes_digest` does not match the canonical_json digest of its predecessor, or a non-genesis envelope is missing a predecessor accessible to the caller. See zh/identity/key-management.md §7.6 and zh/crypto-media/device-lifecycle.md §12 / §12.1. |
series_predecessor_not_found | reason_code | — | schema_validation, state_resolution | `ak.schema.key_backup.v1.supersedes` references a backup_id that is unknown to the server, deleted, or owned by a different actor / series. Wire endpoint returns 409 Conflict; receivers MUST treat the chain as broken. See zh/crypto-media/device-lifecycle.md §12.1. |
series_seq_not_monotonic | reason_code | — | schema_validation, state_resolution | A PUT /_arkret/self/keys/backups/{backup_id} request whose `series_seq` is not strictly greater than the current maximum sequence within the same (actor_id, series_id), or whose genesis envelope sets series_seq != 0. See zh/crypto-media/device-lifecycle.md §12.1. |
service_identity_conflict | code | 409 | both | The service registration key or declared public base is already bound to a different DID, inception operation, or control root. Providers MUST return this error instead of minting a fork. |
service_identity_provider_unavailable | code | 503 | service_call | The configured Service Identity Provider is temporarily unreachable or unavailable. Callers may retry without changing the registration request. |
service_identity_unavailable | code | 503 | both | The target service cannot serve the request because its runtime service identity is not ready. The response SHOULD carry Retry-After when retry timing is known. |
service_key_revoked | reason_code | — | federation_transaction, auth_decision | Diagnostic key-state fact. On closed peer event submit, a revoked origin service signing key fails current transport authentication as signature_invalid before any idempotency lookup; this reason is not a replay-success response. See zh/conformance/conformance-vectors.md §3.9. |
service_not_plaintext_visible | reason_code | — | service_call, auth_decision | Service is not in the Realm's plaintext_visible_services policy; plaintext-bound operation refused. |
service_prerotation_invalid | reason_code | — | identity_resolution, service_call | A service did:webvh inception or rotation omitted the sole next-key commitment, supplied more than one update/next key, or failed to open the previous nextKeyHashes commitment. Providers and resolvers MUST fail closed as service_registration_denied. |
service_registration_denied | code | 400 | both | A service-registration request is well formed but its signed inception, service type, canonical public base, or control proof violates the Provider profile. |
service_route_fork | reason_code | — | identity_resolution, federation_transaction | The method-native service evidence conflicts with the durable accepted DID history prefix. |
service_unavailable | code | 503 | both | The service or a required upstream dependency is unavailable; callers MAY retry according to Retry-After / next_retry_at when provided. |
session_focus_already_committed | reason_code | — | event_envelope | A subsequent `ak.call.state` event attempted to write a `session_focus` value different from the already-committed one. The reducer MUST `failed_precondition` — `session_focus` is write-once per call lifecycle; in-session focus migration is not supported in v1. See zh/crypto-media/call-state.md §4.1. |
session_focus_no_split_brain | reason_code | — | service_call | The committed `ak.call.state.session_focus` is authoritative and write-once: once it exists, a connect / token-exchange failure against that focus MUST be surfaced as focus-unavailable (`focus_unavailable_for_client`) and clients MUST NOT silently fall back to a different focus to keep the media path up. Naming the invariant explicitly closes the split-brain attack where two subsets of a conference converge on different SFUs. See zh/crypto-media/media-service-binding.md §5 and §2 (`foci[].health_endpoint`). |
session_grant_not_found | code | 404 | endpoint | `ak.gate.account.command.revoke_session.v1` targeted a session grant that is unknown, already inactive, or not owned by the current principal. Implementations SHOULD use a uniform response shape that does not disclose another principal's session grant existence. |
session_grant_replay_expired | code | 410 | endpoint | A durable exact-replay lookup found the original SessionGrant issuance outcome, but its immutable expires_at is past. The response details MUST include grant_id and state=expired. The issuer MUST NOT return the expired credential as success or issue a replacement under the same request identity; the client must perform full authentication with a new one-shot proof and request identity. |
session_grant_replay_indeterminate | code | 409 | endpoint | The stable SessionGrant request identity is older than the issuer's durable replay-record retention window, so the issuer cannot prove whether the one-shot request previously committed. It MUST fail closed and MUST NOT treat the request as first issuance; the client must obtain a new one-shot proof and request identity. |
session_grant_replay_terminal | code | 409 | endpoint | A durable exact-replay lookup found the original SessionGrant issuance outcome in revoked or superseded state. The response details MUST include grant_id and the exact terminal state. The issuer MUST NOT return the credential as success or issue a replacement under the same request identity; the client must perform full authentication with a new one-shot proof and request identity. |
session_logged_out | code | 400 | endpoint | Session-grant rotation refused because the underlying Auth Server browser session has been logged out (finished). The rotation chain cannot be resumed; full re-authentication is required. See account-lifecycle §4.1. |
session_missing | reason_code | — | direct_conversation_readiness | Closed target-specific Direct Conversation readiness blocker: the otherwise authorized Agent has no current session for the requested send path. It MUST NOT appear in generic Agent readiness. |
session_revoke_selector_conflict | code | 422 | endpoint | `ak.gate.account.command.revoke_session.v1` supplied more than one mutually exclusive selector (`target_session_grant_id`, `target_device_id`, `all_sessions=true`) or otherwise failed selector closure. Receivers MUST reject instead of choosing one selector implicitly. |
sfu_not_allowed | code | 403 | service_call | Requested SFU or media focus is not allowed by Realm policy or media service binding. |
sidecar_create_denied | reason_code | — | auth_decision, state_resolution, service_call | Agent Sidecar ensure was denied without revealing whether the controller's native Sidecar or requested source-context mapping already exists. Returned as a generic failed_precondition sub-reason to avoid existence side channels. See zh/models/sidecar.md §3 and §7. |
signal_class_denied | code | 403 | service_call | ak.self.signal.command.send.v1 rejected the envelope because an outer-visible signal_class, current participant/membership/scope authority, or Realm policy gate failed. The Station cannot inspect encrypted product kind or target; all such outer admission failures use this non-enumerating code without naming the failed input. |
signal_plaintext_forbidden | reason_code | — | service_call, client_sync | Sub-reason for failed_precondition when any plaintext broadcast envelope is submitted or received. Signal is encrypted-only in every scope; implementations MUST fail closed and MUST NOT advertise Signal for a scope unless they can verify its MLS basis, AAD, and proof. See zh/sync/signal.md §1 and §3. |
signal_rail_unavailable | code | 503 | service_call | ak.self.signal.command.send.v1 could not enqueue or fan out the transient signal because the signal channel is temporarily unavailable; durable Event history is not affected. |
signal_ttl_out_of_range | code | 400 | service_call | ak.self.signal.command.send.v1 rejected the outer envelope lifetime because it is absent when required or outside the service's advertised signal_class-specific range. |
signature_invalid | code | 401 | both | A required signature or proof does not verify. |
signature_window_invalid | code | 401 | endpoint | An RFC 9421 HTTP Message Signature failed the freshness window: created/expires missing or not integers, the claimed lifetime outside the registered ceiling, created outside the registered receiver skew, or expires already reached. It also covers byte-identical replays after the bounded replay cache evicted the entry. The window algorithm and its numerals are owned by http_signature_contract_registry in registry/contract-registry.json and stated in zh/sync/service-http-binding.md section 8.3; no other page or artifact carries those numerals. Scenario-specific tightening is registered as a freshness profile there, currently only for ak.peer.signal.command.relay.v1. Applies to every registered signing scenario, including Applet transaction push (zh/extensions/applet-integration.md section 7.3.1) and MIMI provider-to-provider writes (zh/extensions/mimi-interop.md section 5). |
snapshot_capacity_exceeded | reason_code | — | event_envelope, auth_decision | The candidate RealmCommit would make the maximal-disclosure, current governance Station-signed inline Realm State Snapshot exceed 8 MiB of complete RFC 8785 canonical signed bytes. Every operation branch that first admits an Event and signs a RealmCommit rechecks the final durable cut in the same transaction; overflow returns top-level failed_precondition (HTTP 409) with this reason_code and zero Event, RealmCommit, typed current, and snapshot writes. Exact 8 MiB is allowed. See zh/conformance/scalability-constraints.md section 4.1 and fixtures/scalability-limits-fixture.json. |
soft_logged_out | code | 401 | both | Session was soft-logged-out and must be refreshed. |
source_refs_unverifiable | code | 400 | service_call | Directory ingest or cross-service projection could not verify declared source_refs against the authoritative Station, resource source, or signed projection checkpoint. |
space_already_terminal | reason_code | — | event_envelope, auth_decision | `ak.space.tombstone` rejected because the target Space is already `tombstoned`. |
space_has_live_dependents | reason_code | — | event_envelope, auth_decision | Space tombstone is blocked by a non-tombstoned child Space or an effective canonical placement of a non-redacted Strand. Archived dependents still count. See zh/models/realm-and-space.md section 3.4. |
space_not_active | reason_code | — | event_envelope, auth_decision | `ak.space.archive` rejected because the target Space is not in `active` state (per zh/models/realm-and-space.md §3.3). |
space_not_archived | reason_code | — | event_envelope, auth_decision | `ak.space.restore` rejected because the target Space is not in `archived` state; `tombstoned` is a terminal state and MUST NOT be restored. |
space_parent_cycle | reason_code | — | event_envelope, auth_decision | `ak.space.parent` would create a cycle in the Space ancestor chain (self-loop or chain loop). Reducer MUST reject (zh/models/realm-and-space.md §3.5). |
space_parent_mismatch | reason_code | — | event_envelope, state_resolution | The expected_parent_space_id declared by an ak.space.parent payload and the parent_space_id stored in that Space's space_parent register are not byte-equal. Both are always present -- the payload member is required and the register is written for every Space by the ak.space.create genesis write -- so this is a two-valued equality, not the three-valued stored_field_matches_payload the directed-invite slot needs. Reducer MUST reject with zero writes (zh/models/realm-and-space.md section 3.5). |
space_parent_unreadable | reason_code | — | event_envelope, auth_decision, projection | Canonical parent or placement structural facts cannot be read or verified at the operation basis. Structural validation and subtree authorization MUST fail closed, without disclosing hidden target Realm identity. |
space_realm_mismatch | reason_code | — | event_envelope, auth_decision | A verified canonical Space parent or Board/List/Strand placement crosses actual Realm identities. Reject with failed_precondition after target readability and evidence checks; no profile exception or automatic Realm rewrite. |
spam | reason_code | — | moderation_report | Standard moderation reason: spam content. |
state_mismatch | code | 409 | both | Encrypted content or MLS epoch is bound to an application state root that cannot be verified against accepted state. |
status_unavailable | code | 503 | service_call | Required credential revocation or status material is temporarily unavailable. |
storage_failed | reason_code | — | event_envelope, service_call | Capture artifact persistence or deletion failed in the Arkret blob pipeline. |
strand_already_terminal | reason_code | — | event_envelope, auth_decision | A `ak.redaction` event targeting a Strand is rejected because the target Strand is already in terminal state `redacted`. Strand terminal state is reached via ak.redaction. |
strand_not_active | reason_code | — | event_envelope, auth_decision | `ak.strand.archive` / `ak.strand.update` rejected because the target Strand is not in `active` state (per zh/models/common-fields.md §5.1). |
strand_not_archived | reason_code | — | event_envelope, auth_decision | `ak.strand.restore` rejected because the target Strand is not in `archived` state. |
stream_dropped | code | 409 | endpoint | A subscription stream dropped events or account updates and the client must follow the server-directed recovery path. |
stream_resync_required | code | 409 | endpoint | A subscription stream cannot safely continue from the supplied cursor; the client must resync from a fresh cursor or snapshot. |
stream_tail_missing | reason_code | — | client_sync | One authorized commit stream cannot be continued from the caller's durable cursor: the tail range between that cursor and the stream's current head is unavailable to this service (retention or history floor reached, or the durable range is otherwise not servable). It is per-stream and scoped to exactly one stream_ref, so the client re-acquires the snapshot and tail for that stream alone and MUST NOT reset other Realm / Circle / Sidecar streams, the account baseline or to-device ACKs. It is NOT the same as an empty tail: an authorized stream that is simply at its head returns zero items and `accepted`. emitting this code for a stream the caller may not read, or for one that does not exist, would turn that anti-enumeration bucket into an oracle. Only a stream the caller is currently authorized to read may be reported with it. See zh/sync/client-sync.md section 3. |
structure_depth_exceeded | reason_code | — | encoding, schema_validation | A canonical JSON or deterministic CBOR structure exceeds the v1 maximum nesting depth of 64 (objects and arrays combined, top-level container = depth 1). Receiver MUST reject (top-level schema_violation) before recursive descent can exhaust the stack, and MUST NOT truncate or partially parse. See zh/conformance/scalability-constraints.md section 2. |
superseded | reason_code | — | device_recovery | Recovery-session `rejection_reason_code` value: the session was superseded by a newer recovery session for the same principal / device. Closed value set defined in artifacts/schemas/recovery-session.schema.json; completion ownership is defined in zh/identity/security-transactions.md §2. |
superseded_by_repairing | reason_code | — | event_envelope, auth_decision | Reducer audit reason stamped when one controller-signed ak.agent.key.authorize runtime-replacement Event atomically observe-removes every prior active authorization dot named by its exact supersedes[] set and adds the new authorization. No synthetic ak.agent.key.revoke Event is authored. Sessions issued from superseded keys MUST fail closed within the revocation freshness window. See zh/identity/key-management.md §3.6.1. |
temporarily_unavailable | code | 503 | endpoint | The service is temporarily unavailable or cannot currently satisfy the request. |
test_signing_material_denied | reason_code | — | auth_decision, identity_resolution, proof_verification, crypto | The presented signing material or identifier is registered in artifacts/registry/test-material-registry.json as published test material or as a reserved test identifier, so it MUST NOT be admitted on a formal verification, authorization or trust-admission path even when the signature verifies: its private key ships with the specification. The refusal is fail-closed and MUST NOT leave a verified binding, a resolution cache entry or accepted auth state behind, MUST NOT degrade to a weaker evidence class, a limited_trust pin or a retryable unavailable, and MUST NOT be reachable through a configuration switch on the formal API. See zh/identity/did-usage-and-verification.md §8. |
third_party_invite_acceptance_missing | reason_code | — | service_call, auth_decision | Sub-reason for failed_precondition when ak.open.third_party_invite.command.activate.v1 has no usable Station acceptance attestation for the named invite: the attestation signature does not verify against the attesting Station service identity, its audience or verification_id is not this service, or it does not attest a pending ak.invite.third_party on an accepted basis. A caller-reported invite id, a caller-computed invite digest, an Event signature or an HTTP success MUST NOT be accepted in its place. See zh/sync/third-party-invites.md section 7.5. |
third_party_invite_acceptance_stale | reason_code | — | service_call, auth_decision | Sub-reason for failed_precondition when a Station acceptance attestation is structurally valid but its signed expires_at has passed, observed_at is later than expires_at, or expires_at exceeds invite_expires_at. The verification service MUST fail closed rather than bind private invite material with an invalid or expired attestation. See zh/sync/third-party-invites.md section 7.5. |
third_party_invite_material_mismatch | reason_code | — | service_call, auth_decision | Sub-reason for failed_precondition when the accepted ak.invite.third_party attested for activation differs from the frozen provisioning record in author, Realm, expiry or any member of the public third_party_invite object. The verification service MUST refuse rather than adopt the accepted typed current result values, because its private token, salt or pepper and ephemeral key were generated for the frozen material. See zh/sync/third-party-invites.md section 7.5. |
third_party_invite_provisioning_already_bound | reason_code | — | service_call, auth_decision | Sub-reason for duplicate_conflict when an activation attempt would bind an already bound provisioning record to a different invite, Realm or author. One provisioning record binds to exactly one invite for its whole life; the service MUST NOT rebind, rotate the ephemeral key or reissue the token. See zh/sync/third-party-invites.md section 7.5. |
third_party_invite_provisioning_expired | reason_code | — | service_call, auth_decision | Sub-reason for failed_precondition when activation is attempted after the provisioning record activation_expires_at. The record is cleaned up on the zh/sync/third-party-invites.md section 6.1 schedule and MUST NOT be revived; the inviter provisions fresh material and authors a new invite. |
third_party_invite_token_in_query | reason_code | — | service_call, auth_decision | A 3PID invite claim arrived with the invite_token sourced from a URL query string or path segment instead of from a URL fragment or out-of-band code, in violation of zh/sync/third-party-invites.md §3.2. The verification service MUST reject and SHOULD invalidate the token to prevent referer / log replay. |
timeout | code | 504 | both | A wait-for, long-poll, or upstream dependency timed out. |
token_expired | reason_code | — | service_call, auth_decision | A media backend join token presented at connect time is past its `expires_at` (e.g. a LiveKit JWT whose `exp` has elapsed, distinct from `proof_invalid` which covers a structurally bad / wrong-issuer signature). The client MUST re-run the media-service-binding §3 token exchange instead of reusing the stale token; clients MUST NOT extend or replay an expired backend token. See zh/crypto-media/bindings/livekit.md §8 and zh/crypto-media/bindings/arkret-native.md. |
token_issuer_unauthorised | reason_code | — | service_call, auth_decision | A media token's `service_signature.kid` or `participant_binding.issuer_kid` resolves to a service DID that does NOT appear in the current epoch `ak.realm.media_service.service_id` (or the foci[]-aligned token endpoint authority commit). Clients MUST reject — this closes the attack where any service can forge a focus join token. See zh/crypto-media/media-service-binding.md §3. |
too_large | code | 413 | both | Generic request, query, batch, or envelope size limit exceeded. More specific blob/push/payload variants may be used when available. |
track_disabled | code | 409 | both | The target Strand track has enabled=false and does not accept new writes (synthesis edits or track-scoped patches). Generic freeze code for any track; discussion_track_disabled is the discussion-track-specific specialization for ak.message.* writes. See zh/models/strand-and-message.md §4.1 / §4.7. |
transcription_artifact_pipeline_bypassed | reason_code | — | service_call | A backend attempted to deliver a transcription artifact outside the Arkret-side blob pipeline, or used a key not derived from the MLS-Exporter label `ak.rtc-transcript-key/v1` (e.g. reused the SFrame / recording label or an empty Context). Clients MUST fail closed. See zh/crypto-media/call-state.md §5.1 and zh/crypto-media/media-service-binding.md §8.1. |
transcription_denied | reason_code | — | service_call, auth_decision | Call transcription was requested without `ak.call.transcribe` capability, or the Realm policy forbids transcription. Issuer / reducer MUST reject; parallels `recording_denied` for the transcribe dimension. See zh/crypto-media/call-state.md §5.1. |
ttl_expired | reason_code | — | event_envelope, auth_decision, state_resolution | A bounded-lifetime artefact (member application, reservation typed current result, presign envelope, runtime gate proof, etc.) is past its declared `expires_at` / TTL window. See per-feature spec sections. |
turn_credential_expired | code | 401 | endpoint | TURN REST-style ephemeral credential is past its TTL; client MUST request a fresh credential. See zh/crypto-media/webrtc-signaling.md §4.1. |
unauthenticated | code | 401 | both | Authentication material is missing or invalid. |
unknown_event_kind | reason_code | — | event_envelope | Event kind does not appear in the current registry. Fail closed; an unknown kind is not a non-critical extension and current-v1 has no generic critical_extensions carrier that can make it admissible. |
unknown_field | reason_code | — | event_envelope | Current parser rejected an Event carrying a top-level or payload field not declared by the closed schema for its kind (including removed / renamed fields in artifacts/registry/forbidden-wire-fields.json). Distinct from `schema_violation` in that it pinpoints an unrecognized field rather than a constraint violation on a known field. See zh/overview/current-contract.md §1. |
unknown_focus_type | reason_code | — | service_call, schema_validation | A `ak.realm.media_service.foci[].focus_kind` value is not in the v1 registered set (`livekit` / `mediasoup` / `janus` / `arkret_native` / `moq_relay`) or is registered but not supported by this client / issuer. Clients MUST fail closed instead of forwarding the token to an arbitrary SDK. See zh/crypto-media/media-service-binding.md §2. |
unknown_kind | reason_code | — | event_envelope | Current parser rejected an Event whose `kind` is not an active registered v1 kind. Sync, federation, snapshot, SDK, and conformance paths MUST fail closed and MUST NOT perform payload-shape disambiguation or alias lookup. See zh/overview/current-contract.md §1. |
unrecognized_endpoint | code | 404 | endpoint | The path is inside the protocol namespace but not implemented by the service. |
unresolved_basis | reason_code | — | state_resolution, projection | A projected RSVP head references a schedule basis that cannot be resolved, is invisible, or is not on the target Strand's schedule revision DAG. The head is retained for audit, excluded from the effective response, and MUST NOT be guessed into currency. See zh/models/calendar-event.md. |
unsupported_aead_profile | reason_code | — | crypto, schema_validation | Receiver does not recognise `encryption.aead.aead_profile` (or sees a reserved-but-unpublished profile such as `ak.aead.hybrid_kem.*`). Receivers MUST fail closed; inferring parameters from `aead.name` alone is forbidden. See zh/identity/key-management.md §7.9. |
unsupported_attachment_scheme | reason_code | — | schema_validation, service_call | An encrypted-attachment envelope carries a `scheme` value the receiver does not recognise. Receivers MUST fail closed rather than guess a decryption form. See zh/crypto-media/media-and-blob.md §3.2/§3.3. |
unsupported_ciphersuite | code | 422 | both | An MLS ciphersuite selector is not an active row of artifacts/registry/mls-ciphersuite-registry.json (unknown, inactive, or reserved-but-not-activated) during KeyPackage publish, claim or Commit submission. One of the four algorithm-agility fail-closed errors; dual-registered as a service code and a reason_code (see unsupported_digest_algorithm). Receivers MUST fail closed even if the underlying MLS library supports the suite. See zh/crypto-media/encryption-and-audit.md §2.1. |
unsupported_content_encoding | code | 415 | both | A canonical non-streaming JSON operation carried a Content-Encoding header. Canonical JSON bindings MUST reject the request before reading or decompressing the body; see zh/conformance/scalability-constraints.md section 2.1.4. |
unsupported_did_method | code | 422 | endpoint | The DID method is syntactically valid but not supported by the active identity registry or resolver policy. |
unsupported_digest_algorithm | code | 422 | both | The digest suite prefix in a typed digest value (e.g. sha256:<hex>, cbor.sha256:<hex>) is not an active row of artifacts/registry/digest-suite-registry.json supported by the receiver (unknown id, unregistered tuple, or reserved suite) on a critical field. See zh/conformance/encoding.md §3.1-§3.2. Dual-registered (also a reason_code): all four algorithm-agility fail-closed errors (unsupported_digest_algorithm / unsupported_signature_alg / unsupported_hpke_suite / unsupported_ciphersuite) appear in both `codes` (top-level service error) and `reason_codes` (per-item sub-reason); see zh/conformance/schema-registry.md §1.1.1. |
unsupported_event_kind | code | 501 | both | The service does not accept the requested active standard Event kind. |
unsupported_feature | code | 501 | both | A required protocol feature is not supported. It also carries the per-item rejection for a structurally valid Event whose producer class or wire feature has no admissible v1 form, such as an rfc9420.proposal decoded to an unsupported RFC 9420 sender class or Proposal type (mls-proposal-admission-registry.json); that case MUST NOT be reported as schema_violation. Dual-registered as a reason_code for the event_envelope scope (see reason_codes[]). |
unsupported_hpke_suite | code | 422 | both | HPKE suite id on an application-layer committed surface is not an active row of artifacts/registry/hpke-suite-registry.json (unknown, inactive, or reserved-but-not-activated). One of the four algorithm-agility fail-closed errors; dual-registered as a service code and a reason_code (see unsupported_digest_algorithm). Receivers MUST fail closed rather than infer suite parameters from the AEAD name. See zh/identity/key-management.md §7.5.2. |
unsupported_join_rule | code | 422 | both | A third-party (3PID) invite claim targeted a join-rule Realm whose continuation profile is outside v1 base conformance (e.g. knock_restricted) and the deployment has not declared the required candidate profile in ak.find.directory.read.describe.v1 / ak.account.describe. The verification service MUST reject the token claim instead of silently downgrading. See zh/sync/third-party-invites.md §4.3. |
unsupported_media_policy | code | 422 | endpoint | The requested media, call, recording, or SFU policy is not supported by the service or negotiated media profile. |
unsupported_operation_version | code | 422 | both | The supplied exact versioned operation selector is duplicated, unknown, not advertised on the selected carrier, or outside the selected endpoint family; this code also applies when the endpoint family has no advertised exact candidate. Receivers MUST fail closed without version fallback, alias resolution, or payload-shape inference. |
unsupported_organization_registration_scope | code | 422 | endpoint | A requested delegated scope is outside the closed organization registration scope set, or this deployment does not offer it. The scope vocabulary is closed so that a deployment cannot mint administrative authority the organization never consented to. |
unsupported_profile | code | 422 | both | The selected operation or portable artifact requires a profile or feature the service does not implement. Capabilities use supported_features / supported_profiles. The server MUST fail closed and MUST NOT substitute a local default or permissive interpretation. Dual-registered as a per-device reason_code for ak.edge.push.command.notify.v1 (see reason_codes[]). |
unsupported_profile_patch_path | code | 422 | endpoint | `ak.self.account.command.update_profile.v1` received a patch path outside the account self-service allowlist (`display_name`, `avatar_blob_ref`, `profile_fields.<key>`). Handle, lifecycle, principal, actor_kind, accountability, authorization, and handle-claim paths MUST be rejected instead of silently ignored. |
unsupported_proof_profile | code | 422 | service_call | Wallet and verifier have no mutually supported proof profile for the requested presentation. |
unsupported_protocol_version | code | 422 | both | A syntactically well-formed protocol-family bootstrap discriminator is not supported by the receiver. For v1 ServiceDescribe and equivalent ping surfaces, a protocol_version string other than the canonical value 1.0 makes the complete service unusable before capability intersection or route caching. Missing or non-string values remain schema_violation. A binding that implements Arkret-Protocol-Version header or equivalent media-type negotiation returns this code for an unsupported requested version. Dual-registered as a top-level service code and a reason_code. See zh/overview/current-contract.md §1 and §4. |
unsupported_signature_alg | code | 422 | both | Proof / event signature `alg` is not in the conformance signature-algorithm allowlist (artifacts/registry/signature-alg-registry.json) on a critical field. One of the four algorithm-agility fail-closed errors; dual-registered as a service code and a reason_code (see unsupported_digest_algorithm). See zh/conformance/encoding.md §6.1. |
untrusted_backup_signature | reason_code | — | crypto, device_recovery, state_resolution | A key-backup envelope signature verifies cryptographically but the signer is not an active accepted device in the current generation, or is revoked, unauthorized, or generation-mismatched. Receivers MUST reject it even if the series chain and ciphertext_digest are self-consistent. See zh/identity/key-management.md §7.4.1. |
upstream_unavailable | code | 503 | endpoint | A required upstream dependency is temporarily unavailable. HTTP bindings MUST use 503 Service Unavailable and SHOULD include Retry-After when a retry window is known; 412 is reserved for failed request preconditions. |
verification_method_principal_mismatch | reason_code | — | auth_decision, service_call | A request supplied a `verification_method` whose DID component does not bit-identically match the target principal id after stripping fragment/query. Surfaces in two places. (1) `ak.gate.account.command.pair_agent_key.v1`: `verification_method` vs `agent_id`. (2) `ak.gate.account.command.issue_session_grant.v1` agent branch (`proof.proof_kind="agent_key_proof"`): `proof.verification_method` vs request `principal_id`. Endpoints MUST fail closed before invoking the proof validator so that mismatch is reported as this code rather than as a generic signature failure. See zh/identity/key-management.md §3.6.1. |
verifier_unauthorized | code | 403 | service_call | A presentation verifier could not prove authority to represent its claimed organization or relying party. |
view_already_terminal | reason_code | — | event_envelope, state_resolution | An ak.view.update or ak.view.reconcile targeted a View whose accepted lifecycle state is tombstoned, or attempted to restore that View to active. Tombstoned shared Views are terminal. See zh/models/views.md §3.1. |
watch_level_public_must_be_self | reason_code | — | auth_decision | `ak.strand.watch.set` writing `level_public=true` for another actor is rejected — publishing one's own subscription level is an opt-in personal disclosure and MUST be written by the target actor themself. `ak.strand.watch.set.others` writes MUST omit `level_public` or set it to `false`. See zh/models/strand-and-message.md §8.4. |
watch_must_be_self | reason_code | — | auth_decision | `ak.strand.watch.set` may only set the watch state of the submitting actor; cross-actor writes require `ak.strand.watch.set.others` (audit / accessibility scope). See zh/models/strand-and-message.md §8.4. |
watch_muted_must_be_self | reason_code | — | auth_decision | `ak.strand.watch.set` writing `level="muted"` for another actor is rejected — `muted` suppresses mention / moderation / workflow notifications and MUST be opt-in by the target actor themself. `ak.strand.watch.set.others` only authorizes writing `level ∈ {mentions_only, participating, all}` for other actors. See zh/models/strand-and-message.md §8.4. |
watch_set_others_audit_missing | reason_code | — | auth_decision | A `ak.strand.watch.set` write that uses `ak.strand.watch.set.others` to set another actor's watch state was rejected because it lacked semantic_refs[role=audit_pair] to a same-batch `ak.audit.accessed` event, or the paired audit payload did not identify the same writer DID, target actor DID, typed current result id, paired event id/digest, and before/after heads. See zh/models/strand-and-message.md §8.4. |
webvh_cache_too_stale | reason_code | — | auth_decision, service_call | A did:webvh resolver cache entry exceeded the per-entry maximum evidence age, even if the global cache-only outage window has not expired. High-risk writes, service delegation, capability reconstruction, and snapshot witness acceptance MUST fail closed. See zh/identity/identity-did.md §5. |
webvh_cache_unavailable | reason_code | — | identity_resolution | did:webvh resolution is in cache-only degraded mode and either the requested operation is outside the closed low-risk read-only set, or no committed, controller-proof-verified cache evidence is available. Resolver MUST fail closed rather than treat unresolvable as valid. |
webvh_witness_controlling_organization_unverified | reason_code | — | identity_resolution, auth_decision | A deployment profile requires witnesses from distinct controlling organizations, but the controlling organization of at least one witness cannot be verified, or two witnesses resolve to the same organization. Counting unverifiable or colliding organizations would let one operator running several witness keys satisfy a distinct-organization requirement alone; the verifier MUST fail closed on high-risk paths. See zh/identity/identity-did.md §3.4.2. |
webvh_witness_evidence_stale | reason_code | — | identity_resolution, auth_decision | Witness evidence is older than the effective max age set by deployment / Realm policy, measured from when the proof was observed rather than from when any Arkret receipt was signed. Re-signing an old observation does not refresh it. See zh/identity/identity-did.md §3.4.2. |
webvh_witness_parameter_malformed | reason_code | — | identity_resolution, auth_decision | parameters.witness is present but is not the did:webvh 1.0 shape {threshold, witnesses:[{id}]}: threshold outside 1..witnesses.length, a duplicated witness id, a witness id that is not a did:key, a did:key whose multibase/multicodec payload does not decode to a well-formed public key compatible with the log's Data Integrity cryptosuite, or an unregistered extension key. Key decoding happens during parameter validation, not at signature time; accepting a witness id on string shape alone would let an unverifiable key occupy a threshold slot and hollow out the threshold. Also raised when parameters carries a look-alike key such as witnesses, witness_threshold or witnessThreshold, because that shape is evidence the log was produced against a non-standard dialect and the true policy is therefore unknown. A verifier MUST fail closed and MUST NOT fall back to treating the DID as unwitnessed: silently reading a malformed or aliased declaration as threshold 0 turns a DID that declares witnesses into one that requires none. See zh/identity/identity-did.md §3.4.1. |
webvh_witness_proof_invalid | reason_code | — | identity_resolution, auth_decision | A witness proof in did-witness.json fails signature verification, is signed by a key outside the witness listed in parameters.witness, or does not bind the versionId it is offered for. The proof does not count toward threshold and the entry MUST be treated as under-witnessed. See zh/identity/identity-did.md §3.4.1. |
webvh_witness_proofs_unavailable | reason_code | — | identity_resolution, auth_decision | parameters.witness declares a witness policy but the did-witness.json proofs file is unreachable, unparseable, or contains no entry for the versionId under evaluation. Unavailable evidence is not absent policy; the verifier MUST fail closed on high-risk paths rather than proceed as if no witnessing were required. See zh/identity/identity-did.md §3.4.1. |
webvh_witness_threshold_not_met | reason_code | — | identity_resolution, auth_decision | Valid distinct witness proofs for the versionId are fewer than the effective threshold, which is the strictest intersection of the method-native parameters.witness.threshold and the deployment / Realm policy minimum. A holder-declared policy can raise this bar but MUST NOT lower it. See zh/identity/identity-did.md §3.4.1 and §3.4.2. |
welcome_capability_mismatch | reason_code | — | event_envelope, crypto | An MlsWelcomeDelivery claim reference does not match the claimed KeyPackage capabilities. The recipient MUST reject the delivery before decrypting it. |
witness_disagreement | reason_code | — | state_resolution, federation_transaction | Confirmed fork evidence: two byte-distinct canonical Event preimages pass structure, suite and proof prerequisites and independently recompute to the same complete suite-tagged event_id (full-hash collision evidence); two byte-distinct signed RealmCommit objects name the same (stream_ref, stream_position); or a profile declares the observed combination non-joinable. A carried event_id whose recomputed digest differs is only event_id_digest_mismatch and MUST be rejected before quarantine. Two different accepted Events by one actor are not by themselves disagreement: an actor may author any number of Events and event-and-patch.md section 2.6 gives ordering precedence to the RealmCommit alone, so only one stream position carrying two distinct commits is equivocation. Raw stream head differences observed across different replication or disclosure scopes also are not disagreement: a consumer only ever observes the heads of the streams it is granted. The verifier MUST quarantine only the affected evidence scope and fail closed; recovery requires raw replay plus an accepted operator-approved fork resolution. See zh/sync/operations-sync.md §12 and zh/sync/federation.md §4.5.1. |