Skip to content

Error Reference

Every public reject/error id the library can raise, with what raises it and what to do about it.

Error ids are API. Every id below is part of the public API and safe to branch on. The stability promise covers them: while the library is on 0.x, an id can only change in a 0.MINOR.0 release, with a changelog entry. 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.

Each section heading shows the domain name, which is the string you see in logs and devtools. For the realm and connection domains the export you import is shorter: err_realm from @nice-code/realm and err_wire_connect from @nice-code/wire. The action domains are exported under the same name as their domain.

Raised by the realm engine on attach and on each write/intent. A rejection here either rolled back the optimistic effect (reported 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 and is 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 the 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_hydrateA write or intent was queued before the first hydrate, and the hydrate never came (the realm detached, or pendingExpiryMs passed). Nothing was sent.Check realm.status and the connection. Retry after reconnecting if the write still matters.
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 carrier in the preference chain failed.Check the endpoints/network; the reconnection 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.
protocol_version_mismatchA secure peer explicitly reported that the two builds have no compatible first-party protocol generation. Carries the local supported list and bounded remote/observed version details when supplied.Upgrade or roll back one side, then retry explicitly. Carrier fallback and automatic redial park; this cannot silently downgrade to plain HTTP.
peer_identity_mismatchA secure endpoint advertised a different envId, or a different explicitly expected perId. Context contains expected/actual identities and a sanitized destination when available.Fix the route or peer expectation, then retry explicitly. Carrier fallback and automatic redial park.
conflicting_connect_optionsBoth old and new forms were passed together (carriers + transports, reconnect + keepLinkAlive, heartbeat + linkKeepalive, or both cache-key names).Pass one form. Prefer the new names.
dial_unavailableThe carrier has no valid endpoint right now (its request callback returned null).A policy signal, not a failure. The reconnection 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 run over 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, so 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 in wiring or registration. The validation and reliability ids are the ones you branch on.

err_nice_action — runtime & registration: not_implemented, action_id_not_in_domain, domain_already_exists_in_hierarchy, domain_name_collision, 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, runtime_reset, runtime_disposed, ambiguous_action_runtime, invalid_server_runtime_scope, conflicting_server_options, duplicate_action_executor, peer_handler_already_bound, conflicting_connect_options, no_client_runtimes_registered, connection_required. The ids most often handled at an application boundary are:

  • ambiguous_action_runtime: more than one global runtime can execute an untargeted action. Dispatch through runtime.run(request) or pass targetLocalRuntime explicitly.

  • runtime_disposed: code tried to target or mutate a terminal runtime. Construct a new runtime; disposal is idempotent but cannot be reversed.

  • invalid_server_runtime_scope: a serve* host received a global runtime. Construct it with { scope: "instance" } so host handlers stay outside global dispatch.

  • conflicting_server_options: a modern serve* call received both identity and runtime, or neither. Pass exactly one. Prefer identity; use runtime for a caller-owned instance runtime.

  • duplicate_action_executor: two distinct local handlers claim the same domain/action route at the same specificity. Keep one exact executor. An action-specific route may still override a domain fallback.

  • connection_required: a channelCases handler called setState, clearState, or pushBack after arriving over HTTP, or after its duplex binding was dropped/replaced. Use a duplex endpoint for socket state and pushes, or guard the operation with ctx.connection.

  • action_input_validation_failed and action_output_validation_failed: a schema check failed (HTTP 400 for input, 500 for output). Fix the payload.

  • action_input_validation_promise and action_output_validation_promise: 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 three reliable-delivery ids you branch on:

  • reliable_outbox_overflow: the peer isn’t acknowledging, so the send was rejected to bound memory. Reconnect and re-sync.
  • reliable_delivery_abandoned: the send wasn’t acked within its deadline, and 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 seen through the connection’s reconnect handling).

initialization_failed means every transport in the chain failed. 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/dictionary/security, not the address.
break;
case EWireConnectFailureKind.protocol_version_mismatch:
// The secure peers have no common protocol generation. Upgrade or roll back one side.
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.
protocol_version_mismatchThe backend explicitly reported no common secure-wire generation.Compare each build’s WIRE_SUPPORTED_PROTOCOL_VERSIONS; deploy a tested compatible pair.
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 your branch means the same 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.