Errors & codes

Every stable identifier in one place: the 16 socket error codes with handling advice, the close codes, HTTP statuses, and the result/termination enums.

Error shapes

jsonc
# Every REST error uses the envelope; errorCode mirrors the HTTP status:
{
  "success": false,
  "response": "error",
  "responseCode": 409,
  "data": null,
  "error": { "errorCode": "#409", "errorMessage": "user is already in an active game" }
}
# Internal (coding) errors add error.trace for debugging.

# Every socket error is an error message with a stable code:
{ "type": "error", "room": "match_abc", "code": "illegal_move", "message": "illegal move" }
# room is omitted when the failure is session-scoped (queue, play_bot, subscribe …).
# Branch on code — message is human text and may change.

Socket error codes

CodeOnMeaningHandling
invalid_requestanymalformed message (unknown type, bad JSON shape)client bug — log it; nothing to retry
not_foundsubscribeno such room / no own game to re-attach toroute the user to the lobby
not_activegame intentsthe game is already finishedrefresh from game_state; show the result
not_startedmovethe presence gate hasn't opened — status active but started:falseshow the ready/waiting UI; send accept if your UX auto-confirms
not_your_turnmoveit is the opponent's turnsnap the piece back
invalid_movemovethe string is not UCI at allclient bug — validate before sending
illegal_movemovesyntactically fine, illegal in the position (includes a missing promotion letter)snap the piece back; check isPromotion()
draw_too_soonoffer_drawa move must pass between offers (rate limit)disable the offer button until the next move
no_draw_to_claimclaim_drawthreefold / 50-move is not actually on the boardonly enable claim when your move list shows eligibility
too_late_to_abortabortboth sides have already movedoffer resign instead
already_in_gamequeue · play_botone active game per user (also: entered in an exclusive tournament)subscribe() with no room to re-attach to the active game
already_queuedqueuedouble enqueuetreat as success, or offer cancel
rate_limitedqueueper-session enqueue rate limit (each enqueue costs a platform call)back off; never loop queue/cancel
bots_unavailableplay_botno Stockfish worker availablehide/disable bot play; retry later
platform_unavailablequeue (rated)the engine could not reach your /users/{userId} within ~2–3ssurface a retry; fix platform availability — there is deliberately no client-side fallback
spectator_limitsubscribeper-game spectator cap reachedtell the user the game is full

Close codes

CodeMeaningHandling
4001session token expiredSTOP retrying with this token. Ask the platform for a fresh one (re-login / re-mint), then reconnect. The SDK emits authExpired and suppresses auto-reconnect.
4002replaced by the user's own newer socket (second tab / device — newest wins)Do NOT redial, or the two tabs kick each other forever. The SDK emits replaced and suppresses auto-reconnect.
othernetwork drop, server restart, …Auto-reconnect with the same token (the SDK does, with exponential backoff). Your own game is restored; re-subscribe spectates and re-queue on open.

HTTP statuses (REST)

StatusMeaningHandling
400malformed payloadfix the request shape
401 / 403service auth failure — missing/wrong X-Service-Auth, or IP not whitelistedfix credentials/network config; POST /v1/sessions additionally fails closed when the engine's secret is unconfigured
404resource not found (match, tournament, puzzle)check the id
409conflict — user already in a game / already in a tournament / duplicate joinexpected during normal operation; show the conflict to the user
422validation — unknown variant, timeControl, or tournament formatoffer only values from GET /v1/game-types
503engine not ready, or Stockfish unavailable (review)retry with backoff

ℹ Callback direction (engine → your webhook): your responses drive the engine's retries — 2xx/409 delivered · permanent 4xx dead-letters · 5xx/timeout retries. See Callbacks & reliability.

Value enums

FieldValuesWhere
resultwhite_win · black_win · draw · abortedgame_over (socket) — aborted only there; the game_result callback never fires for aborted games (match_aborted does)
result (callback)white_win · black_win · drawgame_result
terminationcheckmate · resign · timeout · stalemate · insufficient · threefold · fivefold · fifty_move · seventy_five · draw_agreement · abortgame_result — how the game ended
reason (socket)the termination values plus no_showgame_over
match_aborted.reasonno_show · abortedno_show = the gate was never confirmed (not a connection failure); aborted = pre-play abort intent
gateready_check · grace_period · nonegame_state — which presence gate applies
fairplay_flag.flagTyperepeat_pairing · sandbag_pattern · lopsided_pair · engine_assistfairplay_flag callback