discordjs/discord.js · error · Error
WebSocketShard wasn't connected
Error message
WebSocketShard wasn't connected
What it means
WebSocketShard.send() requires an open WebSocket connection. If this.connection is null (shard never connected, was closed, or is mid-reconnect), the payload cannot be delivered and it throws.
Source
Thrown at packages/ws/src/ws/WebSocketShard.ts:457
return { ok: false };
} finally {
if (timeout) {
clearTimeout(timeout);
}
this.timeoutAbortControllers.delete(event);
// Clean up the close listener to not leak memory
if (!closeController.signal.aborted) {
closeController.abort();
}
}
}
public async send(payload: GatewaySendPayload): Promise<void> {
if (!this.connection) {
throw new Error("WebSocketShard wasn't connected");
}
// Generally, the way we treat payloads is 115/60 seconds. The actual limit is 120/60, so we have a bit of leeway.
// We use that leeway for those special payloads that we just fire with no checking, since there's no shot we ever
// send more than 5 of those in a 60 second interval. This way we can avoid more complex queueing logic.
if (ImportantGatewayOpcodes.has(payload.op)) {
this.connection.send(JSON.stringify(payload));
return;
}
if (this.#status !== WebSocketShardStatus.Ready && !ImportantGatewayOpcodes.has(payload.op)) {
this.debug(['Tried to send a non-crucial payload before the shard was ready, waiting']);
// This will throw if the shard throws an error event in the meantime, just requeue the payload
try {
await once(this, WebSocketShardEvents.Ready);
} catch {
return this.send(payload);View on GitHub (pinned to a81ed8a306)
Solutions
- Wait for the shard's 'ready' event/status before sending payloads
- Check that the connection exists / shard status before send, or wrap sends in try-catch and requeue
- Stop send loops (intervals) on shard destroy/close events
- Let the manager-level strategy handle reconnection before resuming sends
Example fix
// before
shard.send({ op: GatewayOpcodes.PresenceUpdate, d: presence });
// after
if (shard.status === WebSocketShardStatus.Ready) {
shard.send({ op: GatewayOpcodes.PresenceUpdate, d: presence });
} else {
pendingPayloads.push({ op: GatewayOpcodes.PresenceUpdate, d: presence });
} Defensive patterns
Strategy: validation
Validate before calling
function canSend(shard: WebSocketShard): boolean {
return shard.status === WebSocketShardStatus.Ready;
}
if (canSend(shard)) shard.send(payload); Type guard
function isConnected(shard: WebSocketShard): boolean {
return shard.status === WebSocketShardStatus.Ready;
} Try / catch
try {
await shard.send(payload);
} catch (e) {
if (e instanceof Error && e.message.includes("wasn't connected")) {
requeue.push(payload); // resend once shard is ready again
} else throw e;
} Prevention
- Wait for the shard ready event before issuing gateway commands
- Clear intervals/timers that send payloads when a shard is destroyed
- Queue outbound payloads during reconnects instead of sending blindly
When it happens
Trigger: Calling send() (directly or via identify/resume/heartbeat) while the shard is disconnected — before connect() completes, after a close event, or during a reconnect window.
Common situations: Sending presence updates or gateway commands from app code right after startup before READY; sending after the socket dropped and auto-reconnect hasn't finished; firing send from a timer that outlives the shard's lifetime after destroy().
Related errors
- Tried to connect a shard that wasn't idle
- ClientNotReady
- Cannot destroy VoiceConnection - it has already been destroy
- Session not available
- No session available
AI-assisted analysis of discordjs/discord.js@a81ed8a306 (2026-08-30).
Data as JSON: /api/errors/4a06e191de3f5bd8.
Report an issue: GitHub.