Recipes

Resume Sessions After a Restart

Survive socket drops, fresh Lavalink sessions, and full process restarts with the right layer.

"Resuming" is really three different failure modes, each handled at a different layer. They are not alternatives — you generally enable the first two together and reach for the third when the process itself dies.

What brokeProcess alive?Lavalink keeps the session?Handled by
Socket blipYesYesresumable (Lavalink resume)
Fresh session (Lavalink restarted / window expired)YesNobyLibrary (library replay)
Process restartNo—Rebuild from storage

resumable configures the node's Lavalink session to stay alive for timeout seconds if the socket drops. Reconnect within that window and Lavalink hands the same session back — the ready event arrives with resumed: true and the audio never stopped.

lavalink-resume.ts
const  = ({
  ,
  : [{ : 'localhost', : 2333, : 'youshallnotpass' }],
  : {
    : {
      : true,
      : 60,
    },
  },
});

This only covers a transient socket drop while your process stays up. If Lavalink itself restarts, or the reconnect misses the window, the session is gone and the next layer takes over.

Layer 2: Library replay (byLibrary)

When the node reconnects on a fresh session (resumed: false) and Hoshimi still holds players in memory, byLibrary replays that state onto the node — reconnecting each player, destroying the empty ones, resending the voice state, syncing the queue and replaying the current track.

library-resume.ts
const  = ({
  ,
  : [{ : 'localhost', : 2333, : 'youshallnotpass' }],
  : {
    : {
      : true, // Layer 1 first...
      : true, // ...and this is the fallback when the session is fresh.
      async (, ) {
        // Your own reconnect logic, then hand off to the built-in one.
        await (, );
      },
    },
  },
});

byLibrary needs the players in memory

byLibrary replays what the running process still holds. It fires only on a fresh session (!resumed) and only when there are players to replay — so it complements resumable, it does not replace it. If the whole process restarted, manager.players is empty and there is nothing to replay; that is Layer 3.

resumeFn runs only while byLibrary is enabled, and defaults to resumeByLibrary when omitted — so leaving it out keeps the built-in behavior.

Layer 3: Rebuild from storage

If the process restarts, the in-memory queue is gone, so neither layer above has anything to resume. Point a queue storage adapter at a durable backend, and after you recreate the player, repopulate its queue from what was persisted.

rebuild-queue.ts
// Repopulate the in-memory queue from whatever the storage adapter persisted.
await ...({ : true });

syncCurrent: true also restores queue.current; see Restore the current track and position to resume playback from where it left off.