Skip to content

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 below
if (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.

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).

IdWhat emits itWhat to do
schema_mismatchClient 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_unknownThe server hosts no realm with that id.Fix the realm id, or serve that realm on the DO.
unauthorized_avatar_typeThe avatar’s type isn’t in the realm’s permitted set.Connect as a permitted avatar type.
avatar_unresolvedThe connection’s identity mapped to no avatar (resolveAvatar returned nothing).Ensure the connection carries an authenticated coordinate, or set avatarType.
insufficient_securityThe connection’s negotiated wire level is below the realm’s minimum.Raise the connection’s securityLevel (e.g. encrypted). Fail-closed by design.
unauthorized_coordinateA write targets a path this avatar’s rules forbid.Expected on a rules violation — reflect it in UI; don’t retry blindly.
value_invalidA written value doesn’t match the schema.Fix the value shape; this is a program error.
limits_exceededAn ingress limit was hit (frame bytes / patches / write rate).The client coalescer usually splits automatically; a bare breach means back off.
intent_unknownAn intent name the realm doesn’t define.Fix the intent name (it’s part of the schema hash).
version_gap_unrecoverableThe client’s version is behind the replayable window.Automatic — the client full-rehydrates; no app action.
write_before_hydraterealm.update() ran before the first hydrate (store still {}).await realm.whenLive() first, or use assertOnAttach for state that must exist on attach.
frame_too_staleAn 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_overflowToo many unconfirmed durability: "reassert" frames (a long outage).Re-sync from your own source of truth after reconnecting.
frame_invalidA malformed/hostile realm frame (fuzz gate).Not app-actionable — the codec dropped it. Investigate a buggy/hostile peer.
write_target_missingA 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.

Raised while opening or maintaining a wire connection (createWireClient / connectChannel).

IdWhat emits itWhat to do
connect_exhaustedEvery transport in the preference chain failed.Check the endpoints/network; the keep-alive ladder retries a duplex link.
connect_closedconnect() on a client already close()d — terminal.Build a new client; a closed one never reconnects.
dial_failedOne transport’s dial or handshake failed (a chain-item detail inside connect_exhausted).Read endpoint + kind (see below), then reason.
endpoint_answered_non_protocolSomething 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_unreachableNothing 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_rejectedThe 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_unavailableThe 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_unsupportedA 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_onlyA 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_blockedA 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_mismatchA rehydrated hibernation binding has an unknown schema version.Automatic — the socket is treated as fresh and re-handshakes.
identity_pin_mismatchThe 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).

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;
}
}
kindWhat it meansFirst thing to check
endpoint_answered_non_protocolAn 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_unreachableNothing answered at all.Is the server running, on that port, reachable from this device?
handshake_rejectedThe backend answered and refused.Client/server dictionaryVersion and security level; for a pinned identity see identity_pin_mismatch.
unknownThe 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)”
IdWhat emits itWhat to do
standard_schemaA Standard-Schema validation failed (the /hono adapter, action input/output).HTTP 400 — the message carries the field issues; fix the payload.
  • 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 + isUnknownOutcomeRejection from @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.