Files
fluxer/fluxer_docs/src/content/docs/gateway/opcodes-and-close-codes.md
T

17 KiB

title, description
title description
Opcodes and close codes Numeric registry for main Gateway opcodes, close codes, and exact close reasons.

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

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 Server to client
13 Voice Signal Client to server
14 Lazy Request Client to server
15 Request Guild Counts Client to server
16 Request Channel Member Counts Client to server
28 Request Forum Unreads 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

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 Gateway Error object Fluxer reports a refused voice join that the client is expected to act on
13 Voice Signal object The client sends peer-to-peer signalling to one other voice connection
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
28 Request Forum Unreads object The client requests unread counts for posts in one forum or media channel

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. Fluxer never sends Opcode 5

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. The values 17 through 27 and every value above 28 are undefined.

Fluxer resolves an inbound payload in this order.

  1. A payload that has no d reaches no command handler. Identify closes with 4005. Every other opcode, defined or not, closes with 4001.
  2. Heartbeat and Resume are handled whether or not a session is attached.
  3. Identify closes with 4005 when a session is already attached.
  4. Every remaining opcode closes with 4003 while no session is attached.
  5. With a session attached, Presence Update, Voice State Update, Voice Signal, Request Guild Members, Lazy Request, Request Guild Counts, Request Channel Member Counts, and Request Forum Unreads are handled. Every other opcode, including a server opcode and an undefined value, closes with 4001.

Request Forum Unreads from a user session that did not set CHANNEL_THREADS also closes with 4001.

:::note[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. :::

Gateway Error

Opcode 12 reports a refused Voice State Update command to the session that sent it. The payload has no s and no t, is not replayed by Resume, and closes nothing.

Field Type Description
code string The refusal code
message string A description for diagnosis, which a client does not show or parse
{
  "op": 12,
  "d": {
    "code": "VOICE_P2P_CONSENT_REQUIRED",
    "message": "This call is peer-to-peer and requires agreement to join"
  }
}

Fluxer sends three codes.

  • VOICE_P2P_CONSENT_REQUIRED means the channel or call is peer-to-peer and the join did not send p2p: true. Ask the user, then send the join again with p2p: true.
  • VOICE_P2P_UNAVAILABLE means Fluxer declined peer-to-peer for a join into a peer-to-peer channel or call. Abort the join and tell the user.
  • VOICE_CHANNEL_FULL means the channel is at its user_limit, or the peer-to-peer mesh is at its participant cap. Abort the join and tell the user.

Every other voice refusal sends no payload. Peer-to-peer voice states when each of the first two codes is produced.

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 An undefined or server opcode, a payload with no d, or opcode 28 without thread access or with a non-object 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

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.

:::caution[Reconnecting unchanged reproduces 4004, 4010, and 4012] A client changes the token, the shard pair, or the version before it reconnects. :::

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 seq above the last sequence the session dispatched closes with 4007.
  • The acknowledged sequence. A seq below the last acknowledged sequence closes with 4007. 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

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 An undefined or server opcode, a payload with no d, or opcode 28 without thread access or with a non-object 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.

:::note[Some refusals send nothing] A held or discarded Identify, an over-budget Presence Update, an over-budget Request Forum Unreads, a dropped bounded request, an unroutable or over-budget Voice Signal, and a rejected voice state update produce no close and no reason. A rejected voice state update sends a Gateway Error for three codes and nothing for the rest. Client commands states which command behaves that way. :::

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.