WorldForge Studio

06 — Troubleshooting & versioning

Install problems, runtime limits, and how SDK versions relate to the server and the world-schema ladder.

Install

404 / E404 on @blueforge-studio/* — the scope must point at packr:

# ~/.npmrc
@blueforge-studio:registry=https://api.packr.blueforge.studio

Anonymous reads work; no token is required to install.

workspace:* or @world-map-editor/* in a consumer install — that would mean a broken package: workspace dependencies are bundled into dist, never published. Reinstall from packr; if you see it, the published tarball is bad — check the publish.yml run for the version that shipped.

Runtime

SymptomCause / fix
WebSocket is not defined from /realtimeNode < 22 has no global WebSocket. Pass webSocketImpl (require('ws').WebSocket) to openRealmProvider, or upgrade to Node ≥ 22.
403 realm_requires_user_sessionThe realm needs a user JWT — API keys are refused by design. Use REST + SSE for document reads.
403 read_only_role on a realm ticketPlayers/observers don't get the shared document; they use client.worlds.get (the server's per-role read model) + the SSE stream.
Realm won't reconnect after idleReconnects fetch a fresh ticket automatically and, as implemented, keep retrying with backoff on any ticket failure (including a refusal) until destroyRealmProvider. If the session was revoked, destroy the provider, re-authenticate and call openRealmProvider again.
WorldForgeApiError 403 missing: ['world:write']The key's scopes don't cover the route — see the authentication guide.
commit_required 400 on worlds.patchPATCH needs exactly one of confirm: true / propose: true.
SSE stream ends without errorThe stream is edge-triggered with no replay and no auto-reconnect: re-open it and re-read GET /worlds/:id to catch up.
McpToolErrorThe tool ran but rejected (e.g. scope, validation) — read .message / .result. Thrown by callTool and callToolRaw. Transport-level failures (401) are not McpToolError and, from this wrapper, not WorldForgeApiError either — they come from the MCP SDK.
Invalid literal value, expected "<current>" on world.schemaVersionYou validated an older document. Run runMigrations first, then safeParseWorld (see 03).
No migration from <v> to <current>runMigrations has no rung for that version — the document is newer than this SDK (upgrade the SDK) or not a world.
WorldForgeConfigErrorcreateClient got no auth option. (More than one is not an error: apiKey wins over jwt over getToken.)

Versions

Three version numbers move independently:

  1. SDK package version (this package, semver). Breaking = any signature change to createClient resources or the entrypoint surface; the raw escape hatch is the stable backstop.
  2. WORLD_SCHEMA_VERSION — the world-document ladder (currently 0.53.0 at the time of this audit), exported from /offline. The SDK bundles the engine, so an SDK release can trail a server that has already moved the ladder. runMigrations brings an older document up; the server rejects documents it doesn't recognise, so migrate before upload. A commit_required/invalid_body error after a server schema bump usually means: upgrade the SDK (or run /offline migrations first).
  3. The API itself — additive routes ship continuously (see docs/api/, kept honest by a CI drift test). New routes appear before the SDK wraps them; client.raw reaches them on day one.

Release practice

No changesets in this repo: bump version in packages/sdk/package.json in the PR that changes the SDK surface. Publishing is automatic — publish.yml runs on main pushes touching packages/sdk/**, builds, and skips versions already on packr (so every main push is safe to re-run). To verify what's live:

curl -s https://api.packr.blueforge.studio/@blueforge-studio%2Fworldforge-sdk \
  | node -e "let s='';process.stdin.on('data',d=>s+=d).on('end',()=>console.log(JSON.parse(s)['dist-tags'].latest))"