Opcodes and close codes¶
An opcode is the number that names a Gateway payload. A close code is the number the Gateway sends when it ends a connection. Every value on this page belongs to the TEBHOS Gateway alone. Gateway overview describes the connection and its state machine, and Client commands describes the payloads for these opcodes.
Opcodes¶
| Op | Name | Direction |
|---|---|---|
| 0 | Dispatch | Server to client |
| 1 | Heartbeat | Bidirectional |
| 2 | Identify | Client to server |
| 3 | Presence Update | Client to server |
| 4 | Voice State Update | Client to server |
| 5 | Voice Server Ping | Reserved |
| 6 | Resume | Client to server |
| 7 | Reconnect | Server to client |
| 8 | Request Guild Members | Client to server |
| 9 | Invalid Session | Server to client |
| 10 | Hello | Server to client |
| 11 | Heartbeat ACK | Server to client |
| 12 | Gateway Error | Reserved |
| 14 | Lazy Request | Client to server |
| 15 | Request Guild Counts | Client to server |
| 16 | Request Channel Member Counts | Client to server |
TEBHOS never sends Opcode 9 with d: true, so an Invalid Session is never an instruction to resume.
Payload and when it is sent¶
| Op | Payload | When sent |
|---|---|---|
| 0 | Event-specific data | TEBHOS sends a named event with t and a session sequence with s. Resumed carries the current sequence without advancing it, and a replayed Dispatch keeps its original sequence |
| 1 | The last processed sequence or null from the client, and null from the server |
The client sends on the advertised schedule. The server requests an immediate heartbeat once 90 per cent of the interval has elapsed since the last acknowledgement |
| 2 | Identify object | The client starts one authenticated session |
| 3 | Presence Update object | The client replaces its session presence |
| 4 | Voice State Update object | The client joins, moves, updates, or leaves voice |
| 5 | Not implemented | Never sent. A client that sends it is closed like any undefined opcode |
| 6 | Resume object | The client restores a retained session on a fresh socket |
| 7 | No d field |
The Gateway asks the client to replace the connection and then closes with 4000. Sent when the node drains, when it transfers the session to another node, and when a Resume from a new socket displaces this one |
| 8 | Request Guild Members object | The client requests bounded member chunks |
| 9 | Literal false |
The session named by Resume could not be found, or an established session ended. Afterwards the socket is unauthenticated |
| 10 | Object with heartbeat_interval |
Sent as the WebSocket is accepted, before any client payload is read |
| 11 | No d field |
Acknowledges an accepted client heartbeat |
| 12 | Not implemented | Never sent. Errors reach the client as a Dispatch or a close frame |
| 14 | Lazy Request object | The client replaces bounded guild subscriptions |
| 15 | Request Guild Counts object | The client requests current guild counts |
| 16 | Request Channel Member Counts object | The client requests channel counts |
The registry is complete. Opcode 13 and every value above 16 are undefined.
TEBHOS resolves an inbound payload in this order.
- Heartbeat with a
dkey is handled whether or not a session is attached. - Identify with
dis handled only while no session is attached. Identify with a session attached, or with nod, closes with4005. - Resume with
dis handled only while no session is attached. Resume with a session attached, or with nod, closes with4005. - Any other payload with no
d, including Heartbeat, closes with4001. - Every remaining opcode closes with
4003while no session is attached. - With a session attached, Presence Update, Voice State Update, Request Guild Members, Lazy Request, Request Guild Counts, and Request Channel Member Counts are handled. Every other opcode, including a server opcode and an undefined value, closes with
4001.
Unknown server opcodes are forward compatible
A client SHOULD log an unknown opcode and ignore the frame, and MUST NOT close or reconnect solely because the server used an opcode newer than this registry.
Close codes¶
| Code | Name | Meaning |
|---|---|---|
| 4000 | Unknown error | Drain, an unclassified session creation error, or a Resume whose retained session could not be reached |
| 4001 | Unknown opcode | The opcode is undefined, is a server opcode, or the payload has no d |
| 4002 | Decode error | The payload size, compression stream, encoding, or command fields are invalid |
| 4003 | Not authenticated | An authenticated command arrived before Identify or Resume attached a session |
| 4004 | Authentication failed | The token is invalid, or it does not own the retained session named by Resume |
| 4005 | Already authenticated | Identify or Resume arrived while a session was attached, or with no d |
| 4007 | Invalid sequence | Heartbeat or Resume supplied a sequence outside the bounds that command accepts, as stated in Invalid sequence |
| 4008 | Rate limited | A concurrent connection, connection payload, source IP payload, session payload, or session count budget was exceeded |
| 4009 | Session timeout | More than 45,000 ms elapsed since the last accepted heartbeat acknowledgement while the Gateway was awaiting one |
| 4010 | Invalid shard | The Identify shard value is not a valid [shard_id, shard_count] pair. shard_count is an integer from 1 to 16,384, and shard_id is a non-negative integer below it |
| 4011 | Sharding required | More than 2,500 guilds resolve to one bot session after the shard filter. Identify with a larger shard_count |
| 4012 | Invalid API version | The v connection parameter is absent or is not 1 |
Code 4006 is unassigned. Code 4013 (Ack backpressure) is reserved in the Gateway and is not currently sent. Event filtering describes how a client bounds the events its session receives.
Session outcome¶
| Code | Resumable | Resulting state |
|---|---|---|
| 4000 | Conditional | Closed. A session attached to the connection remains recoverable |
| 4001 | Conditional | Closed. An already established session stays retained |
| 4002 | Conditional | Closed. An already established session stays retained |
| 4003 | No | Closed. No session exists for this connection |
| 4004 | Conditional | Closed. A failed Resume does not invalidate the session it named. An Identify that fails leaves nothing to recover |
| 4005 | Conditional | Closed. A session established by an earlier Identify or Resume stays retained |
| 4007 | No for that attempt | Closed. The named session stays retained until its window expires |
| 4008 | Conditional | Closed. An already established session stays retained |
| 4009 | Conditional | Closed. An already established session stays retained |
| 4010 | No | Closed. No session was created |
| 4011 | No | Closed. No session was created |
| 4012 | No | Closed before Hello. No session exists |
A close that leaves a session without a socket begins a fresh 60,000 ms retention window, measured from the moment the socket ends. Resumable describes only whether an already established session can still be recovered with Resume. The retention window and the bounded replay buffer in Limits and rate limits still limit what a Resume replays.
Reconnecting unchanged reproduces 4004, 4010, and 4012
Change the token, the shard pair, or the version before you reconnect.
Invalid sequence¶
Close 4007 follows one rule for Heartbeat and another for Resume.
Heartbeat tests the value's type alone, and only once a session is attached. Before a session is attached, the Gateway accepts every d and answers with Opcode 11. With a session attached, it accepts a d that is null or any integer, and every other value closes with 4007. A sequence below the acknowledged sequence leaves that bound unchanged, and any other integer sets it and trims the replay buffer. A heartbeat the session cannot confirm within 5,000 ms, including one that arrives just as the session process ends, also closes with 4007.
Resume tests two bounds, and its seq must clear both.
- The current sequence. A
seqabove the last sequence the session dispatched closes with4007. - The acknowledged sequence. A
seqbelow the last acknowledged sequence closes with4007. A heartbeat with a higher sequence moves this bound.
Close reasons¶
The Gateway sends an exact reason string with every application close.
| Reason | Code | Cause |
|---|---|---|
Invalid API version |
4012 | The v connection parameter is absent or is not 1 |
Too many connections |
4008 | The source IP already holds 256 concurrent Gateway connections |
Encode failed |
4002 | Hello could not be encoded |
Compression failed: zstd-stream |
4002 | Hello could not be compressed on a connection that negotiated zstd |
Payload too large |
4002 | An inbound message exceeded 4,096 bytes on the wire, or 4,096 bytes after decompression |
Decompression failed |
4002 | An inbound compressed message could not be decompressed |
Decode failed |
4002 | The payload is not valid JSON, or decodes to something other than an object |
Invalid payload |
4002 | The decoded object has no op |
Rate limited |
4008 | The connection, source IP, or session payload budget was exceeded |
Unknown opcode |
4001 | The opcode is undefined, is a server opcode, or the payload has no d |
Not authenticated |
4003 | An authenticated command arrived before a session was attached |
Already authenticated |
4005 | Identify or Resume arrived while a session was attached, or with no d |
Invalid identify payload |
4002 | Identify is missing token or properties, or a field has the wrong type |
Invalid shard |
4010 | The Identify shard value is not a valid [shard_id, shard_count] pair |
Sharding required |
4011 | More than 2,500 guilds resolve to one bot session after the shard filter |
Too many sessions |
4008 | The user already holds 100 live Gateway sessions |
Invalid token |
4004 | The Identify token is invalid, or the Resume token does not own the named session |
Failed to start session |
4000 | Session creation returned a failure the Gateway does not classify |
Session start failed |
4000 | Session creation kept failing after its retries |
Invalid resume payload |
4002 | Resume is not an object, or token or session_id is missing or not a string, or seq is missing or not an integer |
Invalid sequence |
4007 | Heartbeat or Resume supplied a sequence outside the bounds in Invalid sequence |
Invalid presence payload |
4002 | Presence Update is not an object, has no status, or has a status string that is not a known value |
Invalid voice payload |
4002 | Voice State Update d is not an object |
Session unavailable |
4000 | The retained session could not be reached while Resume was in progress |
Session drain requested; reconnect to continue |
4000 | Opcode 7 was sent immediately before the close |
Heartbeat timeout |
4009 | No new heartbeat acknowledgement was accepted within 45,000 ms of the preceding one |
Encode failed and Compression failed: zstd-stream are produced only while the Hello frame is written. A later outbound frame that cannot be encoded or compressed is dropped and the connection stays open.
The Gateway holds an Identify and retries it silently after a classified temporary failure, so the connection stays open. That covers a paused rollout, a draining node, an ineligible account, an Identify rate limit, a saturated start budget, and a failed or rate-limited session lookup.
Reason strings are stable wire values. A client branches on the code and MAY record the reason for diagnosis.
Some refusals send nothing
A held or discarded Identify or Resume, an over-budget Presence Update, a dropped bounded request, and a rejected voice state update produce no close and no reason. Client commands states which command behaves that way.
Ordinary WebSocket closes¶
A transport can end with no TEBHOS application close code, as happens on a network failure, an intermediary reset, and an ordinary 1000 or 1001 close. An established session remains available for 60,000 ms after the transport ends, and a later transport end starts a new 60,000 ms window. Neither the window length nor the bounded replay history grows.