Reference

Errors and troubleshooting

Every HTTP status and WebSocket error code, what causes it and how to fix it, plus a checklist for connections that won't open.

HTTP status codes#

Statusdetail (examples)CauseFix
401Invalid App KeyPOST /token with a malformed key, an unknown key, or a key of another Domain.Check the Domain slug in the URL and the key (80 hex characters).
401UnauthorizedMissing, malformed or expired token; or its key was deleted.Exchange the key again. If it still fails, the key no longer exists.
403ForbiddenThe key has no claim of the kind this route needs, or the claim lacks the flag.Check GET /me; link a claim or add the flag on the topic's Main Topic.
403Domain is inactiveThe Domain was deactivated.Contact us.
404Not FoundUnknown Domain, claim or key; a state that doesn't exist (GET, echo).Check the slug, id or selector. For states, 404 is a normal "no value".
409App Key is still linked to a claim; unlink it firstDeleting a linked key.Unlink it from every claim, then delete.
409Claim still has linked App Keys; unlink them firstDeleting a claim that keys are linked to.Unlink the keys first.
409App Key is already linked to another claim of this kind; unlink it firstLinking a key that has a claim of that kind already.Unlink it from the other claim, or link it to that one.
422field errorsInvalid body, path or query: topic, type, key, value size, flags, limit, cursor.Read errors: each field lists what is wrong.

422 responses name the fields:

JSON
{
	"errors": {
		"topic": [
			"must be 1 to 8 levels of a-z, A-Z, 0-9, dash, underscore, joined by `:` (at most 128 characters)"
		]
	}
}

Configs and claim topics take a Main Topic only:

JSON
{
	"errors": {
		"topic": [
			"must be a Main Topic: a-z, A-Z, 0-9, dash, underscore, at most 128 characters (no `:`)"
		]
	}
}

WebSocket error codes#

Sent in <op>:error replies (or error for problems with the whole message). The connection stays open.

CodeCauseFix
forbiddenThe connection's claim lacks the flag on this topic's Main Topic.Grant it, then reconnect: permissions are fixed at connect.
bad_requestInvalid topic, type, key or value; value too large; invalid JSON; not an array; more than 100 ops.Read message.
not_foundtell or echo of a missing state.Normal "no value"; handle it.
unknown_opAn op the broker doesn't know (received_op echoes it), or no op.Check the op name and broker: set is State Broker only, pub Event Broker only.
internalUnexpected server error; the op was not applied.Retry with backoff.

The client libraries add one: closed, when the connection wasn't open or closed before the reply.

WebSocket close codes#

CodeMeaning
1000Normal closure (you called close()).
1006Abnormal closure, no close frame: a refused handshake (as seen by browsers), or a lost network.
1009A frame was too large.
otherServer shutdown or idle timeout. Reconnect with backoff.

A connection won't open#

  1. Is the token fresh? It is checked at the handshake; if your token route caches tokens, an expired one fails. Get a new token per attempt.
  2. Is the URL right? wss://…/api/{domain}/event/connect?token=… or …/state/connect; the token URL-encoded.
  3. Does the key have a claim for this broker? Call GET /api/{domain}/me with the same token: event_claim or state_claim must not be null.
  4. Is the Domain right and active? GET /me answers 404 for an unknown Domain and 403 for an inactive one.

A subscription receives nothing#

  • Wrong broker? Events go through the Event Broker, state changes through the State Broker: a sub on one doesn't see the other's data, even for the same topic name.
  • Wrong level? sub chat:lobby doesn't receive chat:support (a sibling) or chat (a parent). Subscribe to the common ancestor.
  • Subscribed after the publish? Live delivery starts at sub:ok; read history to catch up.
  • Waiting for current values? State Broker sub sends changes only; use tell or a listing.
  • Filtered by your own code? Messages carry their exact topic: a check like event.topic === 'chat' drops chat:lobby.

A state "disappeared"#

  • A TTL or max_states removed it. Removals don't notify. Check the Main Topic's config (GET /state/topics/{topic}): the TTL counts from the last write, so a state written once expires one TTL later even if it is read constantly.
  • Someone deleted it: the delete was notified as a change with value: null.