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

  1. Wait for the shard's 'ready' event/status before sending payloads
  2. Check that the connection exists / shard status before send, or wrap sends in try-catch and requeue
  3. Stop send loops (intervals) on shard destroy/close events
  4. 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

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


AI-assisted analysis of discordjs/discord.js@a81ed8a306 (2026-08-30). Data as JSON: /api/errors/4a06e191de3f5bd8. Report an issue: GitHub.