Error Reference
Every public reject/error id the library can raise — what emits it, and what to do about it.
Error ids are API. Every id below is stable from v1 and safe to branch on; you program against
the id, never the message string (messages are for humans and may be reworded). An error arrives as
a NiceError — branch on it with err.hasId("..."), or narrow to a domain first with
err_domain.isExact(err):
// doc-check: skip — illustrative branching against ids documented belowif (err_realm.isExact(e) && e.hasId("insufficient_security")) { // handle the specific case}Realm rejections arrive on onMutationRejected (their .error is a NiceError); connection
failures reject connect()/dispatch; validation failures come back as a typed action result. The
tables group ids by the package that raises them.
Realm — err_nice_realm
Section titled “Realm — err_nice_realm”Raised by the realm engine on attach and on each write/intent. A rejection here rolled back the
optimistic effect (surfaced via onMutationRejected) or refused the attach (whenLive() rejects,
realm.attachError is set).
| Id | What emits it | What to do |
|---|---|---|
schema_mismatch | Client and server built from different realm definitions (hash mismatch). | Redeploy so both ends share one definition — this is a build-skew bug, never retried. |
realm_unknown | The server hosts no realm with that id. | Fix the realm id, or serve that realm on the DO. |
unauthorized_avatar_type | The avatar’s type isn’t in the realm’s permitted set. | Connect as a permitted avatar type. |
avatar_unresolved | The connection’s identity mapped to no avatar (resolveAvatar returned nothing). | Ensure the connection carries an authenticated coordinate, or set avatarType. |
insufficient_security | The connection’s negotiated wire level is below the realm’s minimum. | Raise the connection’s securityLevel (e.g. encrypted). Fail-closed by design. |
unauthorized_coordinate | A write targets a path this avatar’s rules forbid. | Expected on a rules violation — reflect it in UI; don’t retry blindly. |
value_invalid | A written value doesn’t match the schema. | Fix the value shape; this is a program error. |
limits_exceeded | An ingress limit was hit (frame bytes / patches / write rate). | The client coalescer usually splits automatically; a bare breach means back off. |
intent_unknown | An intent name the realm doesn’t define. | Fix the intent name (it’s part of the schema hash). |
version_gap_unrecoverable | The client’s version is behind the replayable window. | Automatic — the client full-rehydrates; no app action. |
write_before_hydrate | realm.update() ran before the first hydrate (store still {}). | await realm.whenLive() first, or use assertOnAttach for state that must exist on attach. |
frame_too_stale | An optimistic frame expired before an authoritative answer — an unknown outcome. | Re-read state; do not blindly re-issue a non-idempotent intent (it may have committed). Detect with isUnknownOutcomeRejection. |
durable_backlog_overflow | Too many unconfirmed durability: "reassert" frames (a long outage). | Re-sync from your own source of truth after reconnecting. |
frame_invalid | A malformed/hostile realm frame (fuzz gate). | Not app-actionable — the codec dropped it. Investigate a buggy/hostile peer. |
write_target_missing | A well-formed write whose target’s parent no longer exists (a pruned entry / a transient region reset by a wake). | Re-read state; an assertOnAttach re-creates a pruned own-entry. Distinct from frame_invalid. |
Connection — err_nice_wire_connect
Section titled “Connection — err_nice_wire_connect”Raised while opening or maintaining a wire connection (createWireClient / connectChannel).
| Id | What emits it | What to do |
|---|---|---|
connect_exhausted | Every transport in the preference chain failed. | Check the endpoints/network; the keep-alive ladder retries a duplex link. |
connect_closed | connect() on a client already close()d — terminal. | Build a new client; a closed one never reconnects. |
dial_failed | One transport’s dial or handshake failed (a chain-item detail inside connect_exhausted). | Read reason; usually a bad URL or an unreachable host. |
dial_unavailable | The carrier has no valid endpoint right now (createRequest returned null). | A policy signal, not a failure — the keep-alive loop parks until the next explicit connect()/dispatch. |
protocol_unsupported | A protocol frame sent on a connection whose peer never advertised it. | Register the protocol before the handshake (dial with the realm/devtools mux attached). |
protocol_on_exchange_only | A prefixed protocol registered on an exchange-only (request/reply) connection. | Thrown at bring-up, not connect — a protocol needs a duplex carrier (a realm can’t ride HTTP-exchange alone). |
protocol_security_blocked | A protocol frame on a non-authenticated connection that didn’t opt into allowPlain. | Raise the connection’s security level, or the protocol must opt in (devtools does). |
binding_version_mismatch | A rehydrated hibernation binding has an unknown schema version. | Automatic — the socket is treated as fresh and re-handshakes. |
identity_pin_mismatch | The peer rejected the handshake: this client’s verify key ≠ the one pinned for its identity (TOFU). | Permanent for this identity+key pair — the redial loop parks. Recover under a new persistent id, or clear the peer-side pin. Branch with err.hasId("identity_pin_mismatch"). |
Actions — err_nice_action, err_nice_transport, err_nice_transport_ws
Section titled “Actions — err_nice_action, err_nice_transport, err_nice_transport_ws”Raised by the action runtime and its transport. Most are program errors (wiring/registration); the validation + reliability ids are the ones a consumer branches on.
err_nice_action — runtime & registration: not_implemented, action_id_not_in_domain,
domain_already_exists_in_hierarchy, domain_no_handler, hydration_domain_mismatch,
hydration_action_state_mismatch, hydration_action_id_not_found, no_action_execution_handler,
wire_action_not_payload, wire_not_action_data, client_runtime_already_registered,
client_runtime_not_registered, runtime_reset, no_client_runtimes_registered. The four you’ll
branch on: action_input_validation_failed / action_output_validation_failed (a schema
check failed — HTTP 400/500; fix the payload) and their *_promise variants (a validator returned a
promise, which isn’t supported — make it synchronous).
err_nice_transport — dispatch & reliable delivery: timeout, not_found, unsupported,
initialization_failed, send_failed, invalid_action_response, and the reliable-tier trio you
branch on — reliable_outbox_overflow (the peer isn’t acknowledging; the send was rejected to
bound memory — reconnect/re-sync), reliable_delivery_abandoned (not acked within its deadline;
the stream skipped past it), reliable_stream_closed (the stream was closed while a send was
unacked).
err_nice_transport_ws — WebSocket carrier: ws_disconnected, ws_create_failed, ws_error
(transport-level, usually surfaced through the connection’s reconnect handling).
Validation — err_validation (@nice-code/common-errors)
Section titled “Validation — err_validation (@nice-code/common-errors)”| Id | What emits it | What to do |
|---|---|---|
standard_schema | A Standard-Schema validation failed (the /hono adapter, action input/output). | HTTP 400 — the message carries the field issues; fix the payload. |
Branching helpers
Section titled “Branching helpers”err.hasId("id")— the primary check; true when the error carries that id anywhere in its domain chain.err_domain.isExact(err)— narrow to a domain (err_realm,err_wire_connect, …) before reading its ids, re-exported from the owning package (err_realm+isUnknownOutcomeRejectionfrom@nice-code/realm).isUnknownOutcomeRejection(rejection)— the realm helper for the one case that is genuinely ambiguous (frame_too_stale): treat it as “outcome unknown, re-read”, never as a definite reject.