Remedia

Session lifecycle and recovery

Manage guild ownership, channel moves, socket resumption, and voice recovery.

A Discord session belongs to one client and one guild. It keeps its assigned node until you explicitly migrate it or dispose it. Different clients may use the same guild independently.

Create and release a session

const session = remedia.createDiscordSession({
  guildId,
  channelId,
  userId,
  selfDeaf: true,
  selfMute: false,
  volume: 0.75,
  filterGraph: 'bass=gain=3',
});

try {
  await session.connect();
  await session.play({ url: 'https://media.example.com/track.mp3' });
  await session.wait();
} finally {
  await session.dispose();
}

userId is the bot's Discord user ID. It must match botUserId for a remote client. Creating a second session for the same guild on one client throws. Dispose the first session before replacing it.

disconnect() stops playback and leaves the channel, keeping the wrapper reusable. dispose() also releases the guild registration. remedia.close() disposes every session and closes the node pool.

Session configuration and channel movement

OptionDefaultBehavior
selfDeaftrueDiscord self-deaf voice state
selfMutefalseDiscord self-mute voice state, independent of playback gain
volume1Finite linear gain from 0 through 2
filterGraphnullTrusted FFmpeg graph, applied before playback

setSelfDeaf() and setSelfMute() retain settings before connection and send updated Gateway voice state after joining. Repeating an unchanged value sends nothing. Gain and filters persist across tracks, moves, and transport recovery. Invalid values reject. FFmpeg validates graph syntax when the backend is configured.

await session.move(otherChannelId);
await session.setSelfDeaf(false);
await session.setSelfMute(true);

A move requests fresh Gateway credentials for the new channel and replaces the voice transport. It keeps the source decoder, playback generation, filters, gain, metadata, and paused state. Keep forwarding this bot's raw dispatch packets to remedia.receive() throughout the move.

Concurrent and failed moves

A move to the current destination shares a pending connection or resolves immediately if connected. A newer destination cancels the older attempt with an AbortError. connect() shares the latest attempt. channelId exposes the latest requested destination, including after failure.

Moves have no automatic recovery retry loop. After failure, playback waits behind bounded buffering. Retry connect(), move elsewhere, or disconnect to release it. Disconnect and disposal cancel pending moves. Moves during explicit disconnection or disposal reject.

Reconnection and limits

A dropped Remedia WebSocket makes the node unavailable for new guild assignments. The client reconnects with bounded backoff and attempts to resume its existing server session. Successful resumption retains guild assignments and reconciles authoritative playback snapshots.

OutcomeApplication action
nodeDisconnectedShow the interruption. Existing assignments stay on this node.
sessionResumedContinue using the session objects. Refresh UI from their snapshots.
eventsLostReconcile current state. Historical notifications are not replayed.
sessionLostRecreate sessions and resolve fresh sources when appropriate.

Resume credentials live only in client memory. A client process restart cannot resume them. A server restart destroys its in-memory sessions. Expiry or a changed server identity marks assignments lost.

Disposing a guild while its node is offline queues cleanup until the same server session resumes. If the whole client closes while the node is unreachable, the server releases resources when its resumption timeout expires.

Discord voice server migration

Voice recovery is separate from reconnecting the Remedia WebSocket. Changed Discord credentials cause the assigned backend to replace its voice connection while keeping playback open. connectionState becomes reconnecting, then connected on success. A paused source remains paused.

Delivery waits at a bounded output boundary during the handoff. Packets already accepted by the old transport may be lost, so a brief audible gap is possible. Files, URLs, buffers, and one-shot Readables continue through the existing decoder. No source is reopened or replayed. stop() remains effective during recovery and the stopped source is not restarted.

Disconnect, disposal, and client shutdown cancel recovery. Exhausted retries emit voiceFailed and fail the routed playback output. Native Voice WebSocket Resume can recover within the transport without requesting new bot Gateway credentials. See transport ownership and generations for the Rust-level details.

Move playback to another node

session.migrate(nodeId) explicitly changes the assigned node. It restarts a supported active URL source near its last observed media position, so audible gaps and a new playback generation are expected. Capacity or eligibility failures before cutover leave the source untouched. See migration requirements and failure behavior.

The client does not automatically move existing guilds to another node when one becomes unavailable. Applications choose how to restore playback after a lost session.

On this page