discordjs/discord.js · error · Error
Cannot play a resource that has already ended.
Error message
Cannot play a resource that has already ended.
What it means
AudioPlayer.play() refuses to play an AudioResource whose underlying stream has already finished ('ended'). Once a resource is consumed to completion it cannot be replayed, because the stream data is gone; the library throws instead of silently doing nothing.
Source
Thrown at packages/voice/src/audio/AudioPlayer.ts:380
this.debug?.(`state change:\nfrom ${stringifyState(oldState)}\nto ${stringifyState(newState)}`);
}
/**
* Plays a new resource on the player. If the player is already playing a resource, the existing resource is destroyed
* (it cannot be reused, even in another player) and is replaced with the new resource.
*
* @remarks
* The player will transition to the Playing state once playback begins, and will return to the Idle state once
* playback is ended.
*
* If the player was previously playing a resource and this method is called, the player will not transition to the
* Idle state during the swap over.
* @param resource - The resource to play
* @throws Will throw if attempting to play an audio resource that has already ended, or is being played by another player
*/
public play<Metadata>(resource: AudioResource<Metadata>) {
if (resource.ended) {
throw new Error('Cannot play a resource that has already ended.');
}
if (resource.audioPlayer) {
if (resource.audioPlayer === this) {
return;
}
throw new Error('Resource is already being played by another audio player.');
}
resource.audioPlayer = this;
// Attach error listeners to the stream that will propagate the error and then return to the Idle
// state if the resource is still being used.
const onStreamError = (error: Error) => {
if (this.state.status !== AudioPlayerStatus.Idle) {
this.emit('error', new AudioPlayerError(error, this.state.resource));
}View on GitHub (pinned to a81ed8a306)
Solutions
- Create a fresh AudioResource for each playback instead of reusing one
- Cache the source (file path/URL/Readable) and call createAudioResource() again per play
- If looping is intended, use the 'loop after finish' pattern: subscribe to the player's Idle state and create a new resource
- Check resource.ended before calling play()
Example fix
// before
const resource = createAudioResource('song.mp3');
player.play(resource);
player.on('idle', () => player.play(resource)); // throws second time
// after
player.on('idle', () => player.play(createAudioResource('song.mp3'))); Defensive patterns
Strategy: validation
Validate before calling
function canPlay(resource) { return resource && !resource.ended; }
if (canPlay(res)) player.play(res); else res = createAudioResource(src); Type guard
function isPlayable<T>(r: AudioResource<T> | undefined): r is AudioResource<T> { return !!r && !r.ended; } Try / catch
try { player.play(res); } catch (e) { if (e.message.includes('already ended')) res = createAudioResource(src); else throw e; } Prevention
- Never reuse AudioResource instances; recreate per playback
- Store the source (path/URL) not the resource in caches/queues
- Rebuild resources inside the player 'idle' handler for looping
- Assert !resource.ended before play in dev builds
When it happens
Trigger: Calling player.play(resource) on a resource that already finished playing (resource.ended === true), e.g. reusing a single AudioResource created via createAudioResource() for a second play() call.
Common situations: Replaying a sound effect or song from a cached AudioResource; looping a track by calling play() again with the same resource; playing the same resource on two players sequentially.
Related errors
- Resource is already being played by another audio player.
- Invalid pipeline constructed for string resource '${input}'
- Node type '${type}' does not exist!
- Cannot play audio as no valid encryption package is installe
AI-assisted analysis of discordjs/discord.js@a81ed8a306 (2026-08-30).
Data as JSON: /api/errors/e3a585d560387b1d.
Report an issue: GitHub.