Skip to main content

Operations

EventMinimumRecommended
After serverWithLeastClients1.0 s1.5 s
After first actor registration0.2 s0.5 s
Actor heartbeat—every 4 s
Any client traffic—every 60 s

Common error codes​

Delivered as GENERIC_ERROR_MESSAGE (type 3) with echoed sequence. Names below match the current wire enum; the MAP_* forms are legacy aliases.

CodeNameTypical cause
5INVALID_TOKENMalformed token material, or not a valid app-scoped token
6APP_NOT_FOUNDUnknown app (formerly MAP_NOT_FOUND)
7UNAUTHORIZEDPermission denied — no active app access / tier permission, or no grid permission for the target chunk (see Permissions below)
13GAME_TOKEN_WRONG_SIZEMalformed token material
15INVALID_REQUESTWire layout or field error
18INVALID_APP_IDNot authorized for this app — unknown app or the token is scoped to a different app (app-scope mismatch)
20USER_NOT_AUTHENTICATEDSession not ready (or not an app-scoped token) — mintAppToken, call serverWithLeastClients, wait ~1.5 s, retry
32TOKEN_EXPIREDThe app-scoped token passed its expiresAt — Buddy evicted the session. Mint a fresh token (refreshAppToken for the same app) and re-assign
33APP_PAUSEDThe app is paused (its organization has no funds, reached a spend cap, or its subscription lapsed): nothing it sends replicates. Keep the session and tell the player; the server re-checks with backoff and serves it again once the app is active, without a reconnect (server v0.37.0)

Permissions (always enforced)​

Every spatial message is authorized on the server. The sender must have active app access whose tier includes the relevant permission key (access to move/send events and text, update_voxel_data to edit voxels, use_voice_chat for audio, use_video_chat for webcam video — opcode 143, see Wire formats → Video payload) and the target chunk must be inside a grid where the sender holds that key. A message failing either check is dropped and answered with GENERIC_ERROR_MESSAGE / UNAUTHORIZED (code 7).

A voxel update is held to a stricter rule (replication server v0.34.0): the grid that decides is the most specific one covering the chunk, so a plot or a safe zone nested in the world grid decides its own chunks, the world grid decides only the wilderness, and a chunk no grid covers is refused. While the app has closed its wilderness to building, every voxel update there is refused too. See Which grid decides a voxel write.

New apps are open by default — they ship with a default tier granting the baseline permissions (access, teleport, update_voxel_data, use_voice_chat; not use_video_chat, which an owner grants on a tier deliberately because every receiver pays the egress) and a world-spanning default grid, and granting a player app access automatically grants them all of its permissions — so out of the box any entitled player can move, talk and build anywhere no narrower grid covers. App owners opt into restrictions (safe zones, plot ownership) via the Game API. See Game API → Permissions overview and Grids and permissions.

The first message to a brand-new chunk region may briefly return UNAUTHORIZED while the server loads that region's grid permissions; retry and it succeeds.

Permission denials are transient and self-healing in general: the server periodically re-pulls a denied session's cached permission state (a few seconds of backoff between refreshes), so an entitlement granted after you connected — or one that reached the game database late — starts working without re-authenticating or reassigning. If you receive a stream of UNAUTHORIZED replies, keep sending at a reduced rate for ~10-15 seconds before falling back to a fresh serverWithLeastClients assignment.

App suspended or over budget​

If an app is paused — its organization is out of wallet funds, over a spend cap on the shared environment, or its subscription lapsed — sessions still connect, and nothing the app's clients send replicates. Since server v0.37.0 the server says so: a refused spatial or channel send is answered GENERIC_ERROR_MESSAGE with code 33 (APP_PAUSED), at most once per session every 5 seconds, and the rest are dropped silently. Heartbeats are not refused. The server re-checks with backoff and serves the session again once the app is active, with no reconnect. mintAppToken and gameClientBootstrap report the reason in runtimeGate, so tell the player the world is paused. This is a billing/runtime state, not a client bug: the owner resolves it on the management API (fund the wallet, raise the cap, or renew). See What "access denied" means. (Before v0.37.0 every send was answered INVALID_APP_ID and a session did not recover when the app was re-activated.)

A player whose own access to the app is revoked or suspended is different: their tokens for the app are revoked and the session ends at once; signing in again is refused with ACCESS_REVOKED or ACCESS_SUSPENDED until it is lifted.

Server reassignment​

If Buddy restarts, evicts your session, or you need a different host:

  1. Call serverWithLeastClients on the Game API again (refreshAppToken first if the app token is near expiresAt).
  2. Wait ~1.5 s for the session to be ready.
  3. Resume UDP with the same app-scoped token on the new host and clientPort.

Load shedding — COMMAND_RECONNECT (type 22)​

A server protects itself when it runs hot. If a CPU core or system memory stays high for several seconds, the server reports NearCapacity (so the Game API stops sending it new clients) and, if pressure continues, Full — at which point it sheds some existing clients to recover. A shed client receives a small server→client datagram:

[1B type=22][32B HMAC]

The 32-byte HMAC is HMAC-SHA256 keyed on your 64-octet app-scoped token (the same token you use to sign your spatial messages) over the single type byte. Because only you and the server hold that token, a matching HMAC proves the command came from the server — verify it and ignore the datagram if it does not match. When you receive a valid COMMAND_RECONNECT, follow the Server reassignment steps above: re-query serverWithLeastClients (it will not return the overloaded server) and move your session to the returned host. The losing server keeps serving you for a short grace period (≈5 s) and then deletes your session and ignores further packets, so reconnect promptly. There is no reply to send, no sequence number to echo, and no reason field; it is a one-shot directive.

Native (direct-UDP) clients should verify the HMAC and handle type 22. Browser clients using the GraphQL UDP proxy are moved automatically by the proxy (which verifies the HMAC) and never see this message.

Browser clients​

Use the GraphQL UDP proxy instead of raw UDP.