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 endpoint + kind (see below), then reason. |
endpoint_answered_non_protocol | Something answered and it is not nice-wire — an HTTP error page, a proxy/WAF, a different server. Carries url, httpStatus, contentType, and a capped bodyPreview. | Fix the URL, or whatever sits in front of it. The preview usually names the culprit outright. |
endpoint_unreachable | Nothing answered: DNS, connection refused, TLS, offline, or a socket that errored before opening. Carries url + the platform’s reason. | Start the server, open the firewall, check the network. |
handshake_rejected | The peer is a nice-code backend and refused the handshake (dictionary/security mismatch, a custom gate). | Fix the identity or version — not the address. identity_pin_mismatch is the pinned-key flavour below. |
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 (see My client can’t connect), 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).
My client can’t connect
Section titled “My client can’t connect”initialization_failed means the whole transport chain came up empty. It names the endpoint it was
dialing, and carries a kind so you can branch without matching messages:
// doc-check: skip — illustrative branching against ids documented above// Both are re-exported from @nice-code/action (0.87+), so an actions consumer needs no wire import.import { err_nice_transport, EWireConnectFailureKind } from "@nice-code/action";
if (err_nice_transport.isExact(e) && e.hasId("initialization_failed")) { const { endpoint, kind } = e.getContext("initialization_failed"); switch (kind) { case EWireConnectFailureKind.endpoint_answered_non_protocol: // Something is there, but it is not the backend. Wrong URL, or a proxy/WAF in the way. break; case EWireConnectFailureKind.endpoint_unreachable: // Nothing is there. Server down, wrong port, firewall, offline. break; case EWireConnectFailureKind.handshake_rejected: // It *is* the backend and it said no — identity or version, not the address. break; }}kind | What it means | First thing to check |
|---|---|---|
endpoint_answered_non_protocol | An endpoint answered with something that isn’t a nice-wire frame. | The URL, then anything between you and it (proxy, WAF, tunnel). The error’s bodyPreview usually identifies it. |
endpoint_unreachable | Nothing answered at all. | Is the server running, on that port, reachable from this device? |
handshake_rejected | The backend answered and refused. | Client/server dictionaryVersion and security level; for a pinned identity see identity_pin_mismatch. |
unknown | The chain failed for something not yet classified. | Read cause and the origin error. |
The same kind values apply on both carriers — an HTTP exchange and a WebSocket dial classify
their failures identically, so a consumer’s branch means one thing everywhere.
Holding a dial error from somewhere other than an action call? classifyConnectFailure(error)
returns the same EWireConnectFailureKind for any error (unknown when it can’t tell), and
endpointFromConnectFailure(error) pulls out the dialed URL. The classifier is re-exported from
@nice-code/action; endpointFromConnectFailure lives in @nice-code/wire only — from an action
failure, read endpoint off the initialization_failed context instead, as above. See
Error 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.