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 main Gateway alone. Gateway overview describes the connection and its state machine, and Client commands describes the payloads for these opcodes.
Opcodes
Section titled “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 Session1 | 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 |
1 Fluxer never sends Opcode 9 with d: true, so an Invalid Session is never an instruction to resume
Payload and when it is sent
Section titled “Payload and when it is sent”| Op | Payload | When sent |
|---|---|---|
| 0 | Event-specific data | Fluxer sends a named event with t and a session sequence with s1 |
| 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 advertised interval has elapsed since the last acknowledgement2 |
| 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 closed3 |
| 6 | Resume object | The client restores a retained session4 |
| 7 | No d field | The Gateway asks the client to replace the connection and then closes with 40005 |
| 8 | Request Guild Members object | The client requests bounded member chunks |
| 9 | Literal false | The session named by Resume could not be reached, the supplied seq is below the replay floor, or an established session ended6 |
| 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 count records |
| 16 | Request Channel Member Counts object | The client requests channel count records |
1 Resumed has the session’s current sequence without advancing it, and a replayed Dispatch keeps the sequence it was first sent with
2 Answer a server heartbeat request immediately, in addition to the regular schedule
3 A client that sends Opcode 5 or 12 gets the same close as a client that sent an undefined opcode
4 Resume is accepted whether or not a session is already attached to the connection
5 Opcode 7 precedes the close when the Gateway node is draining, when the node transfers the session to another Gateway node, and when a Resume from a new socket displaces this one
6 After the frame, a socket whose session ended is unauthenticated. After a failed Resume, a socket that already held a session still holds it
The registry is complete. Opcode 13 and every value above 16 are undefined.
Fluxer resolves an inbound payload in this order.
- A payload that has no
dreaches no command handler. Identify closes with4005. Every other opcode, defined or not, closes with4001. - Heartbeat and Resume are handled whether or not a session is attached.
- Identify closes with
4005when a session is already attached. - 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.
Close codes
Section titled “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 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, user 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] pair1 |
| 4011 | Sharding required | More than 2,500 guilds resolve to one bot Gateway session2 |
| 4012 | Invalid API version | The v connection parameter is absent or is not 1 |
1 shard_count is an integer from 1 to 16384, and shard_id is a non-negative integer below shard_count
2 The count is taken after the shard filter, so a bot clears it by identifying with a shard_count large enough that no shard owns more than 2,500 guilds
Code 4006 is unassigned, and no code above 4012 is defined. Event filtering describes how a client bounds the events its session receives.
Session outcome
Section titled “Session outcome”| Code | Resumable | Resulting state |
|---|---|---|
| 4000 | Conditional | Closed. A session attached to the connection remains recoverable1 |
| 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. Resume does not invalidate the session it named2 |
| 4005 | Conditional | Closed. A session established by an earlier Identify 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 |
1 A close that leaves a session without a socket begins a fresh 60,000 ms retention window, measured from the moment the socket ends. A session displaced by a Resume from a new socket is already attached to that socket and enters no window
2 A Resume that fails token verification leaves the named session in place for the rest of its retention window, so a later Resume with the owning token still recovers it. An Identify that fails token verification leaves nothing to recover
Resumable describes only whether an already established session can still be recovered with Resume. The 60,000 ms retention window and the bounded replay buffer described in Limits and rate limits still limit whether a Resume succeeds and what it replays.
Invalid sequence
Section titled “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 Identify or Resume attaches one, the Gateway accepts every d and answers with Opcode 11. With a session attached, the Gateway 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 that arrives in the short window between the session process ending and the socket noticing also closes with 4007.
Resume tests the bounds below 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.
A third bound, the replay floor, produces no close. It is the highest sequence the replay buffer has evicted. When a seq is inside both bounds but below the floor, the Gateway sends Opcode 9 with d: false and the named session stays retained until its window expires.
Close reasons
Section titled “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 encoded1 |
Compression failed: zstd-stream | 4002 | Hello could not be compressed on a connection that negotiated zstd1 |
Payload too large | 4002 | An inbound message exceeded 4,096 bytes on the wire, or exceeded 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 user client 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 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 sessions2 |
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 classify3 |
Invalid resume payload | 4002 | Resume is not an object, or token or session_id is missing or is not a string, or seq is missing or is not an integer |
Invalid sequence | 4007 | Heartbeat or Resume supplied a sequence outside the bounds stated 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 |
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 close4 |
Heartbeat timeout | 4009 | No new heartbeat acknowledgement was accepted within 45,000 ms of the preceding acknowledgement |
1 Both reasons are produced only while the Hello frame is being written. A later outbound frame that cannot be encoded or compressed is dropped and the connection stays open
2 A bot credential is bounded by the guild count that forces sharding, and this budget does not apply to it
3 The Gateway holds the Identify and retries it silently after a classified transient 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 session RPC
4 The Gateway node is draining, the node is transferring the session to another Gateway node, or a Resume from a new socket displaced this one
Reason strings are stable wire values. A client branches on the code and MAY record the reason for diagnosis.
Ordinary WebSocket closes
Section titled “Ordinary WebSocket closes”A transport can end with no Fluxer 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.