Keyboard shortcuts

Press ← or → to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Error reference

This page lists every kind of error Open Play returns, its status code, and its body format. When writing error handling code, first look at which endpoint family (core REST or plugin) is being called: the error bodies of the two families differ.

Warning

Most core REST endpoints return plain-text (text/plain) errors, not JSON. Only the few errors marked “JSON” on this page carry a JSON envelope. Do not parse the error text; decide by the status code and (if present) the JSON code field. Plain-text messages can change between versions.

Error families

FamilyWhich endpointsBody
Core REST/v1/characters…, /v1/wallets…, /v1/store…, /v1/auth/… etc. (endpoints)Plain text; a few errors are JSON {code, message[, module]}
Plugin/v1/plugins/…Always JSON {code, message, retryable, requestId[, reason, module]} + the x-request-id header
Realtime/v1/realtime/wsError frame: {type, requestId?, code, retryable} (protocol)
Chat socket/v1/chat/ws{"type":"error","code":"…"} (realtime overview)

Core REST: status table

StatusBody (plain text unless stated otherwise)WhenRetry?
400A description of the errorMissing or broken header, invalid field, unknown command type, If-Match formatNo; fix the request
401unauthorizedAPI key or player session missing, invalid, expired; x-crx-env missing or broken; a banned account (no distinction is made)No; renew the credentials
403permission deniedThe key type is insufficient (for example a server endpoint with a client key), CSRF or Origin rejectionNo
403JSON {"code":"account_banned","message":"account banned"}On sign-in endpoints, a banned account after ownership has been provenNo; do not build a loop
404not foundThe resource does not exist or was deletedNo
404JSON {"code":"module_disabled","message":"module disabled","module":"store"}The product module is disabled in the environmentNo; enable the module (modules)
409version conflict: expected 3, actual 5The If-Match version does not match the real oneRe-read the state and repeat with the new version
409already exists: <resource>A resource with the same ID/key already existsNo
409idempotent request in progressThe first request with the same Idempotency-Key is still being processedYes, shortly, with the same key
410idempotent request result expiredThe key’s record retention (24 hours) expiredWith a new key, reading the state first
413No bodyThe request body exceeded the 2 MiB limit (the image upload endpoint is subject to its own limit)No
422idempotency mismatchSame key, different contentNo; do not reuse the key
422unprocessable: <reason>A business rule rejection (insufficient balance, stock, limit…)No; do not repeat without changing the state
422rejected: <code>A plugin business rule rejection (through core routes)No
429rate limited or rule budget exceeded: <rule>Rate limit or rule budgetYes, by waiting (rate limits); there is no Retry-After
500internal errorAn unexpected server error; no detail is leakedYes, bounded
503tenant unavailable; header Retry-After: 30The environment’s placement is being moved or is temporarily unavailableYes, after Retry-After

Notes:

  • In body parsing errors, depending on the extractor, 415 (wrong Content-Type) or 422 (JSON that does not conform to the schema) may also be seen. Rely only on the status code for the decision.
  • The 401 and 403 difference: if the identity is missing or cannot be resolved, 401; if the identity is valid but insufficient for this operation, 403. On the player ticket endpoint all identity errors return 403 (realtime protocol).
  • If you call a server endpoint with a client key, you get 403 permission denied; if the key is unknown, it is 401.

Purchase (POST /v1/player/store/listings/{listing_id}/buy) error codes

The player purchase endpoint states the rejection reason with a JSON code: {"code":"out_of_stock","message":"…"} (message is detail text; make your decision by code).

codeStatusMeaning
not_visible404The listing does not exist or is not visible to this player
listing_changed409The listing’s version (expected_version) changed; refresh the catalog and show it again
out_of_stock422Out of stock
limit_reached422The per-player purchase limit is full
insufficient_funds422The balance is insufficient
inventory_full422The inventory is full

Other errors (400 header/body, 401, 403 Origin or CSRF, idempotency mismatch 422) are as in the general table above. An IAP listing cannot be bought from this endpoint (422). Details: store guide.

Plugin endpoints

Plugin endpoints return a JSON envelope; give the requestId value in support requests (the same value is also in the x-request-id header).

{"code":"unprocessable","message":"plugin command rejected","retryable":false,"requestId":"0192a3b4-0000-7000-8000-0000000000d1","reason":"street_reset_too_early"}
codeStatusretryableNote
unauthorized401noSession/key missing or invalid; a client key
forbidden403noCSRF/Origin rejection; the plugin is not enabled; the version grant does not match; a banned account (message: account banned)
module_disabled404noThe module field names the disabled module
not_found404noUnknown plugin or message type
invalid_argument400noBroken/missing header, unknown schema version, a payload that does not conform to the schema
conflict409noThe If-Match version does not match
idempotency_in_flight409yesA request with the same key is being processed
idempotency_mismatch422noSame key, different body
unprocessable422noA business rule rejection; reason is a plugin-specific machine code
idempotency_expired410noThe record expired
rate_limited429yesRetry-After: 60
tenant_unavailable503yesRetry-After: 30
internal500yes

Details and examples: calling plugins.

Note

On a plugin endpoint, a banned account, unlike the core identity endpoints, does not come with the account_banned code: 403, forbidden, and message: "account banned" is returned. You need this distinction only for a user-interface message; do not build a retry loop.

Differences between core and plugin

Core RESTPlugin
BodyPlain text (a few JSON exceptions)Always JSON
Machine codeOnly on errors that are JSONcode on every error
retryableNone; decide by status codePresent
Retry delayOnly 503 (30 s)429 (60 s) and 503 (30 s)
Banned account403 account_banned (on sign-in endpoints)403 forbidden
x-request-idNonePresent

Error handling recommendation

  1. First look at the status code; for 401 renew the session or have the player sign in again; 403 is an authorization or configuration problem (key type, CSRF, module, plugin state).
  2. For 404, read the body: if it is JSON and module_disabled, the module is disabled; otherwise the resource does not exist.
  3. Treat 409 in two separate ways: a version conflict (re-read the state) versus idempotency “in progress” (wait and repeat with the same key).
  4. Use exponential backoff on 429 and 503; if there is a Retry-After, wait for it (the example on the idempotency and concurrency page).
  5. 422 is a business rule rejection; repeating the same request as is does not change the outcome.
  6. The server does not leak error detail; when reporting a problem, give the status code, the request time, and (on plugin endpoints) the requestId (troubleshooting).