Every error LobbyLab reports has a stable code and a page that says what it means and what to do. Codes never change meaning once shipped.
Multiplayer API errors
Sent in the JSON error body with the HTTP status shown. Status: the Multiplayer API is in private alpha.
| Code | HTTP | When |
|---|---|---|
room_type_unknown | 400 | The request names a room type this project does not have. |
invalid_request | 400 | The body, query or a header does not match the route's schema. detail names the field. |
key_invalid | 401 | The key is unknown or malformed. |
key_revoked | 401 | The key was revoked. |
project_expired | 401 | A sandbox project is older than 72 hours. |
token_invalid | 401 | The player token is malformed, signed for another project, or not a token. |
token_expired | 401 | The player token is past its expiry. |
project_disabled | 403 | The project's kill switch is on. |
origin_not_allowed | 403 | The request's Origin is not in the project's list of allowed origins. |
native_anonymous_disabled | 403 | A publishable key was used without an Origin (a native app or a server) and the project does not allow anonymous native play. |
secret_key_in_browser | 403 | A secret key arrived with an Origin header: it was sent from a browser. |
anonymous_disabled | 403 | The project turned anonymous play off. |
room_locked | 403 | The host locked the room: it takes no new members. Seated members can still reconnect. |
banned | 403 | This player was banned from the room. |
not_reserved | 403 | The room was created for chosen players only, and this player is not one of them. |
not_a_member | 403 | A seat renewal or a leave for a room this player is not in (or was removed from). |
rooms_limit_reached | 403 | The project has as many rooms open at once as its plan allows. The answer carries quota. |
ccu_limit_reached | 403 | The project has as many players online at once as its plan allows. The answer carries quota. |
player_rooms_limit_reached | 403 | This player is already in 3 rooms. The answer carries quota. |
capability_missing | 403 | The player token does not grant what this route needs (its can list). |
room_not_found | 404 | No room has this id, or it closed and was deleted. |
code_not_found | 404 | No open room has this code. |
not_found | 404 | No route has this method and path. |
method_not_allowed | 405 | The path exists, but not with this HTTP method. |
room_full | 409 | The room has maxPlayers players, or it has seated 4 times maxPlayers members over its life. |
idempotency_key_reused | 409 | The same Idempotency-Key came with a different body. |
payload_too_large | 413 | The request body is larger than the route accepts. |
unsupported_media_type | 415 | The body is not application/json. |
rate_limited | 429 | A rate limit was hit. The answer carries retryAfterSec (also the Retry-After header) and scope, which names the limit. |
internal_error | 500 | Something failed on LobbyLab's side. |
realtime_capacity_limited | 503 | The server's pool of live rooms is full, or the Free and sandbox share of it is used up. The answer carries retryAfterSec. |
sandbox_capacity_reached | 503 | All sandboxes together are at their ceiling. The answer carries retryAfterSec. |
Socket rejections
A change sent over the socket that the room refuses comes back as a rejection with one of these codes. Clients treat any other code as a generic refusal.
| Code | Meaning |
|---|---|
forbidden_path | The room type's write rules do not let this player write that path. |
guard_failed | A guard in the change did not hold: someone changed the value first. Read the new state and try again. |
version_conflict | ifVersion did not match the room's version: the state moved on. Read it and try again. |
too_large | The change would take the state, the view or a player slot past its size limit. |
rate_limited | The player sent changes faster than the room allows. Send fewer (live rooms: use your player slot). |
invalid_op | An operation is malformed (a bad path, inc on something that is not a number, and so on). |
not_host | Only the host can do this. |
not_a_player | Spectators cannot change the state. |
unknown_intent | The intent name is not one the room knows. |
Removed from a room
A member's sockets close with code 4403 and one of these reasons.
| Reason | Meaning |
|---|---|
left | The player left the room. |
kicked | The host or the game's backend removed the player. |
banned | The player was removed and may not come back. |
room_closed | The room closed. |
origin_not_allowed | The socket's origin is not allowed for the project. |
project_disabled | The project was turned off. |
Build check problems
Problems the build check reports for a game package. Errors stop a game from being published; warnings do not.
Package files
| Code | Severity | Problem |
|---|---|---|
package.missing_directory | error | The package folder does not exist |
package.missing_file | error | A required package file is missing |
package.not_a_file | error | A package file is a folder or a link |
package.file_too_large | error | A package file is over its size limit |
package.file_unreadable | error | A package file could not be read |
package.unpublished_files | warning | client/ has files that are not published |
package.client_pending | warning | Only client/index.html is missing |
manifest.json
| Code | Severity | Problem |
|---|---|---|
manifest.invalid_json | error | manifest.json is not valid JSON |
manifest.missing_field | error | manifest.json is missing a required field |
manifest.unknown_field | error | manifest.json has an unknown field |
manifest.player_range | error | maximumPlayers is below minimumPlayers |
manifest.subscriptions | error | subscriptions is missing a required event |
manifest.bot_count | error | botCount or botsSetting does not fit the player count |
manifest.execution_settings | error | The realtime or timer settings do not fit the execution profile |
manifest.smoke_intents | error | smokeIntents is empty or names a player who has not joined |
manifest.smoke_inputs | error | smokeInputs names a missing player or does not fit the profile |
manifest.smoke_payload | error | A smoke payload is not small plain JSON |
manifest.config_invalid | error | config is not small plain JSON |
manifest.too_large | error | manifest.json is over its size limit |
manifest.invalid_value | error | A manifest.json field has a value that is not allowed |
manifest.no_smoke_inputs | warning | A realtime game has no smokeInputs |
manifest.large_room_untested | warning | Rooms over 8 players are not simulated |
server.ts rules
| Code | Severity | Problem |
|---|---|---|
server.syntax_error | error | server.ts has a TypeScript syntax error |
server.import.forbidden | error | server.ts imports something other than @playable/sim |
server.import.named_only | error | @playable/sim is imported without named imports |
server.import.reexport | error | server.ts re-exports from another module |
server.import.dynamic | error | server.ts uses import() or import.meta |
server.import.require | error | server.ts uses require() |
server.forbidden_api.eval | error | eval() or Function() in the rulebook |
server.forbidden_api.globals | error | process, globalThis, global, Bun or Deno in the rulebook |
server.forbidden_api.network | error | Network access in the rulebook |
server.forbidden_api.timers | error | Timers in the rulebook |
server.forbidden_api.date | error | Date or performance in the rulebook |
server.forbidden_api.random | error | Math.random() in the rulebook |
server.forbidden_api.reflection | error | .constructor, Reflect or Proxy in the rulebook |
server.forbidden_api.console | error | console in the rulebook |
server.rulebook_export | error | server.ts does not export rulebook |
server.tick_missing | error | A realtime rulebook has no tick method |
server.tick_unused | warning | tick is defined but the game is not realtime |
Bundling server.ts
| Code | Severity | Problem |
|---|---|---|
bundle.missing_export | error | An imported name does not exist in @playable/sim |
bundle.build_error | error | server.ts could not be bundled |
bundle.failed | error | Bundling server.ts failed |
bundle.too_large | error | The bundled server is over its size limit |
client/index.html rules
| Code | Severity | Problem |
|---|---|---|
client.external_url | error | The client has an external URL |
client.protocol_relative_url | error | The client has a protocol-relative URL |
client.script_src | error | The client loads a script file other than the kit |
client.legacy_runtime_conflict | error | The client loads both the kit and the legacy 3D runtime |
client.module_import | error | An inline module script uses import or export |
client.syntax_error | error | An inline script has a syntax error |
client.kit_not_loaded | error | Playable.connect is called but the kit is not loaded |
client.not_connected | error | The client never connects to the platform |
client.canvas2d_context | error | A canvas2d game has no 2D canvas |
client.webgl_renderer | error | A webgl game has no 3D renderer |
client.raw_webgl | error | A raw WebGL client misses a required check |
client.navigation | error | The client reloads or navigates its own page |
client.raw_three_renderer | warning | The client creates THREE.WebGLRenderer directly |
client.no_send_input | warning | A realtime client never calls sendInput |
client.legacy_bridge | warning | A realtime client uses the legacy bridge |
client.storage | warning | The client uses browser storage or cookies |
client.parent_frame | warning | The client touches the parent frame |
client.network | warning | The client opens its own network connection |
client/assets
| Code | Severity | Problem |
|---|---|---|
assets.invalid_name | error | An asset name has characters that are not allowed |
assets.link | error | client/assets contains a link |
assets.too_deep | error | client/assets is nested too deep |
assets.unsupported_type | error | An asset has a file type that is not allowed |
assets.budget_exceeded | error | The assets are over their count or size limits |
assets.invalid_glb | error | A .glb file is not valid binary glTF 2.0 |
assets.glb_external_resource | error | A .glb file references external buffers or images |
assets.directory_invalid | error | client/assets is not a normal folder |
assets.unreadable | error | client/assets could not be read |
The smoke run
| Code | Severity | Problem |
|---|---|---|
smoke.init_rejected | error | rulebook.init rejected the game setup |
smoke.join_rejected | error | A join was rejected in the smoke run |
smoke.start_rejected | error | The start was rejected in the smoke run |
smoke.intent_rejected | error | A smoke intent was rejected |
smoke.invalid_state | error | The rulebook returned an invalid state |
smoke.invalid_view | error | rulebook.project returned an invalid view |
smoke.invalid_event | error | The rulebook returned an invalid event |
smoke.too_many_events | error | One transition returned too many events |
smoke.threw | error | A rulebook method threw an error |
smoke.missing_method | error | The rulebook is missing a method |
smoke.not_json | error | A rulebook method returned something that is not plain JSON |
smoke.output_too_large | error | A rulebook method returned more data than its budget |
smoke.random_budget | error | A rulebook method drew too many random numbers |
smoke.timeout | error | A rulebook method did not return in time |
smoke.cpu_budget | error | A rulebook method took longer than its CPU budget |
smoke.worker_crashed | error | A rulebook method crashed the rule worker |
smoke.call_failed | error | A rulebook method failed |
Realtime conformance
| Code | Severity | Problem |
|---|---|---|
conformance.settings | error | The realtime settings cannot be simulated |
conformance.init_rejected | error | rulebook.init rejected session creation |
conformance.init_failed | error | rulebook.init failed in the simulated match |
conformance.room_failed | error | The simulated room could not start |
conformance.join_policy | error | Nobody can join under this joinPolicy |
conformance.join_rejected | error | A player could not join the simulated match |
conformance.start_rejected | error | The host could not start the simulated match |
conformance.intent_rejected | error | A smoke intent was rejected in the simulated match |
conformance.intent_ended_match | error | A smoke intent ended the match before any tick |
conformance.tick_threw | error | rulebook.tick threw an error |
conformance.tick_timeout | error | rulebook.tick did not finish in time |
conformance.tick_invalid_result | error | rulebook.tick returned a result the platform cannot use |
conformance.state_not_json | error | The state is not plain JSON |
conformance.state_too_large | error | The state is over its size limit |
conformance.view_not_json | error | A view is not plain JSON |
conformance.view_too_large | error | A view is over its size limit |
conformance.tick_too_slow | error | rulebook.tick is over the hard time limit |
conformance.checkpoint_failed | error | The final state could not be stored |
conformance.rule_error | error | A rulebook method failed in the simulated match |
conformance.crashed | error | The simulated match crashed |
conformance.no_details | error | The simulated match failed without details |
conformance.tick_over_budget | warning | rulebook.tick averages over its time budget |
conformance.match_ended_early | warning | The match ended itself in under 2 seconds |
conformance.intent_skipped | warning | A smoke intent was skipped in a smaller run |
Performance budgets
| Code | Severity | Problem |
|---|---|---|
performance.tick_cpu | warning | Server tick CPU is over budget |
performance.bandwidth | warning | Bandwidth per player is over budget |
performance.view_size | warning | Views are over budget |
performance.state_size | warning | State is over budget |
performance.server_bundle | warning | The server bundle is over budget |
performance.client_page | warning | client/index.html is over budget |
performance.assets | warning | The assets are over budget |
performance.pixel_ratio | warning | The client renders at the full device pixel ratio |
performance.shadow_map | warning | A shadow map is over budget |
performance.three_objects | warning | The client creates too many 3D objects |
performance.adaptive_quality | warning | Adaptive quality is off |
performance.budget | warning | A performance budget is missed |
The platform
| Code | Severity | Problem |
|---|---|---|
platform.conformance_unavailable | error | Realtime games cannot be verified on this build |
platform.check_failed | error | The check itself failed |
Other problems
| Code | Severity | Problem |
|---|---|---|
check.unclassified | error | A problem without a specific code |