Skip to content

Errors

Every failed message is answered with an error envelope:

{
  "type": "error",
  "requestId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
  "code": "FORBIDDEN",
  "message": "You are not authorized to perform this WebSocket action."
}
  • requestId echoes the failing request's id when the envelope could be parsed.
  • message is a safe, client-facing description — never a stack trace or an internal identifier.
  • Error responses are always delivered on the same connection; an error never terminates the connection (connection-level failures use close codes instead, see connection.md).

Reference: protocol.md · connection.md · authentication.md


Error codes

The canonical error codes are produced by the platform's central WebSocket error mapper.

Code Description HTTP Equivalent Retryable When it occurs
BAD_REQUEST Invalid envelope or event payload. 400 No Malformed JSON, unknown event type, Zod validation failure.
UNAUTHORIZED Authentication is required / invalid token. 401 No (re-authenticate) Token missing, invalid, expired, or wrong type on the message context.
FORBIDDEN Authenticated but not allowed to perform the action. 403 No Missing route permission, room access denied, role restriction.
NOT_FOUND The referenced resource does not exist. 404 No Unknown cycleId, bidId, auction.
CONFLICT The operation conflicts with current state. 409 No Bid rejected by rules, illegal state transition, duplicate idempotency key in a competing window.
RATE_LIMITED Per-user or per-IP limit exceeded. 429 Yes (after window reset) Sliding-window limit exceeded for the event type.
INTERNAL Unexpected server failure. 500 Yes Any unhandled error; the server logs details and returns a safe message.

Retryability

  • RATE_LIMITED and INTERNAL may be retried after backoff.
  • BAD_REQUEST, FORBIDDEN, NOT_FOUND, CONFLICT should not be retried as-is; fix the request first.
  • UNAUTHORIZED requires a fresh token + reconnection, not a naive retry.

Requested/aliased error codes

Some integrations and earlier drafts referenced the following names. They are not emitted by the current server but are part of the historical contract. Map them as follows:

Legacy name Canonical code Notes
INVALID_TOKEN UNAUTHORIZED Emitted at handshake close (1008); a message-level error uses UNAUTHORIZED.
INVALID_PAYLOAD BAD_REQUEST Envelope/data validation failure.
INVALID_BID BAD_REQUEST / CONFLICT Invalid bid shape → BAD_REQUEST; rule rejection (min amount, race) → CONFLICT.
ROOM_NOT_FOUND NOT_FOUND Unknown room name; unauthorized room → FORBIDDEN.
FORBIDDEN FORBIDDEN Same code.
RATE_LIMITED RATE_LIMITED Same code.
INTERNAL_ERROR INTERNAL Same semantics.

Error examples

BAD_REQUEST — invalid payload

{
  "type": "error",
  "requestId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
  "code": "BAD_REQUEST",
  "message": "Invalid WebSocket message."
}

An unknown event type also returns BAD_REQUEST:

{
  "type": "error",
  "requestId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
  "code": "BAD_REQUEST",
  "message": "Unsupported WebSocket event type: auction.dance"
}

UNAUTHORIZED

{
  "type": "error",
  "requestId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
  "code": "UNAUTHORIZED",
  "message": "Authentication is required."
}

FORBIDDEN — permission denied

{
  "type": "error",
  "requestId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
  "code": "FORBIDDEN",
  "message": "You are not authorized to perform this WebSocket action."
}

Room denial:

{
  "type": "error",
  "requestId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
  "code": "FORBIDDEN",
  "message": "Only admins can join the admins room."
}

NOT_FOUND

{
  "type": "error",
  "requestId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
  "code": "NOT_FOUND",
  "message": "Auction not found."
}

CONFLICT — state conflict

{
  "type": "error",
  "requestId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
  "code": "CONFLICT",
  "message": "State conflict."
}

RATE_LIMITED

{
  "type": "error",
  "requestId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
  "code": "RATE_LIMITED",
  "message": "Too many WebSocket messages."
}

INTERNAL

{
  "type": "error",
  "requestId": "req_01HQ5BXWYP1Y5RX0W5X0W5X0W5X",
  "code": "INTERNAL",
  "message": "An unexpected error occurred."
}

Connection-level failures

Failures during the handshake never produce an error envelope; they close the connection with a WebSocket close code:

Close code Reason Meaning
1008 Authentication failed Invalid/missing token, or token not an access token.
1009 Frame larger than the max payload.
1001 Server shutting down (going away).
1000 Clean client-initiated close.

Where errors are raised

Stage Error codes
Envelope parse BAD_REQUEST
Route lookup BAD_REQUEST
Authenticate middleware UNAUTHORIZED
Rate-limit middleware RATE_LIMITED
Validate middleware (Zod) BAD_REQUEST
Authorize middleware FORBIDDEN
Handler (business rules) BAD_REQUEST, FORBIDDEN, NOT_FOUND, CONFLICT
Unhandled INTERNAL

See the routing pipeline in protocol.md and the authorization diagram in diagrams.md.