JR — Join Result codes
JR (Join Result) is the response field the server sets on world-join replies. It travels in two packet shapes:
TTjW(Try-To-Join-World response) — what you get back after sending a join request (TTjW W=…) or a special-world entry (mines(level),nether(level)).GetWorldError— what you get whenGw(Get World) fails before aTTjWever fires.
JR=0 means the join succeeded. Anything else is a refusal — the world won't load and the bot stays on the menu (or, if it was already in a world, just keeps playing where it was).
Code table
JR | Meaning | Typical cause |
|---|---|---|
0 | OK | Join accepted — world streaming begins |
1 | Too many players in server | Subserver at capacity. Try client:setSubserver(name) to a quieter shard. |
2 | Too many players in world | Specific world is full. Public world overflow — try the same world name with a WB > 0 shard (or just retry; quotas drift). |
3 | Maintenance starting | Server is about to bounce. Don't retry immediately; wait for the maintenance window to end. |
4 | Invalid world name | The W= field failed validation: empty string, illegal chars, length, reserved name. |
5 | Account banned | The session's account is banned. Co-fields BanState, BPUR, BPl carry scope / reason / permanent-flag. The runtime auto-transitions the bot to the Banned status. |
6 | Admin locked world | Owner / mod has restricted entry. Even though the world exists, the bot isn't on the allow list. |
7 | Account warning | A soft-warn state on the account. Often clears after re-login; sometimes precedes a full ban. |
8 | World unavailable | Worldfile-side problem: corrupted, mid-restore, missing on the host. Not retry-able from the client. |
9 | Server timeout | Client→server join handshake didn't complete in time. Retry with backoff. |
10 | Incorrect server address | Subserver hint mismatch. Re-resolve via Gw then re-issue TTjW. |
11 | Server exception | Server-side runtime error processing the join. Retry; if persistent, file a bug on the host. |
12 | Custom join fail | Catch-all: world-specific entry rule rejected the request (e.g. password world without a key). |
13 | Already in this world | The session is already inside the requested world. Treated as a no-op; the bot stays in the world without disconnecting. |
14 | Not allowed | Permission denied — locale block, age gate, account-tier restriction, etc. |
Co-fields on JR != 0
When the server denies a join, additional fields may travel alongside JR:
| Field | Type | Used when |
|---|---|---|
WN | string | Always — the world the join attempt was for. |
E / Err | string | Free-form error blurb. Surfaced in the bot's lastError. |
BanState | string | JR=5 — scope of the ban (global, world, chat, …). |
BPUR | string | JR=5 — human-readable ban reason. |
BPl | int | JR=5 — 1 = permanent, 0 = temporary. |
How Seraph reacts
JR=0→ status flips toLoadingWorld, the GW/AE bootstrap burst fires, and inventory streams in.JR=5with ban-fields → status flips toBanned, the bot disconnects, and the dashboard surfaces the ban reason. No automatic retry.JR=13while alreadyInWorld→ logged as"warp failed but still connected",pending_worldis cleared, no status change.- Any other
JR != 0→ status flips toErrorwith a"Join denied: <reason> (JR=N)"summary onlastError. The bot stays connected on the menu and is available to retry.
Reading JR from Lua
The runtime translates JR != 0 into client:lastError() automatically — most scripts only need that. If you want to see the code itself, register a p:TTjW event:
client:on("p:TTjW", function(msg)
local jr = msg.JR or 0
if jr ~= 0 then
print(string.format("join denied JR=%d wn=%s", jr, msg.WN or "?"))
end
end)