discordjs/discord.js · error · Error

Cannot destroy VoiceConnection - it has already been destroy

Error message

Cannot destroy VoiceConnection - it has already been destroyed

What it means

VoiceConnection.destroy() transitions the connection to the Destroyed state and releases resources; calling it on an already-destroyed connection throws this Error, since a second teardown is invalid.

Source

Thrown at packages/voice/src/VoiceConnection.ts:586

	 * @param buffer - The Opus packet to play
	 */
	public playOpusPacket(buffer: Buffer) {
		const state = this.state;
		if (state.status !== VoiceConnectionStatus.Ready) return;
		state.networking.prepareAudioPacket(buffer);
		return state.networking.dispatchAudio();
	}

	/**
	 * Destroys the VoiceConnection, preventing it from connecting to voice again.
	 * This method should be called when you no longer require the VoiceConnection to
	 * prevent memory leaks.
	 *
	 * @param adapterAvailable - Whether the adapter can be used
	 */
	public destroy(adapterAvailable = true) {
		if (this.state.status === VoiceConnectionStatus.Destroyed) {
			throw new Error('Cannot destroy VoiceConnection - it has already been destroyed');
		}

		if (getVoiceConnection(this.joinConfig.guildId, this.joinConfig.group) === this) {
			untrackVoiceConnection(this);
		}

		if (adapterAvailable) {
			this.state.adapter.sendPayload(createJoinVoiceChannelPayload({ ...this.joinConfig, channelId: null }));
		}

		this.state = {
			status: VoiceConnectionStatus.Destroyed,
		};
	}

	/**
	 * Disconnects the VoiceConnection, allowing the possibility of rejoining later on.
	 *

View on GitHub (pinned to a81ed8a306)

Solutions

  1. Check connection.state.status !== VoiceConnectionStatus.Destroyed before calling destroy()
  2. Track destruction with a boolean or set so cleanup runs at most once
  3. Wrap destroy() in try/catch in generic cleanup code

Example fix

// before
connection.destroy();
// after
if (connection.state.status !== VoiceConnectionStatus.Destroyed) {
  connection.destroy();
}
Defensive patterns

Strategy: validation

Validate before calling

import { VoiceConnectionStatus } from '@discordjs/voice';
if (connection.state.status !== VoiceConnectionStatus.Destroyed) {
  connection.destroy();
}

Try / catch

try {
  connection.destroy();
} catch (err) {
  if (err.message.includes('already been destroyed')) {
    // idempotent cleanup: safe to ignore
  } else throw err;
}

Prevention

When it happens

Trigger: Calling connection.destroy() twice — e.g. from both a disconnect handler and cleanup code, or after reconnection logic already destroyed the old connection.

Common situations: Double cleanup in shutdown paths (SIGINT handler + voiceStateUpdate), calling destroy() after subscribe/on('destroyed') already ran, or shared references to a stale connection object.

Related errors


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