Leave voice before closing the server on shutdown

AlreadyConnected kept coming back after every rebuild. Shutdown closed
the HTTP server first, which blocks while the panel's WebSocket is open,
so Docker's grace period ran out and the process was killed before it
ever left the voice channels — and Stoat only clears a bot's voice state
when it disconnects, leaving the stale entry that refuses the next join.

Voice channels are now left first, and each shutdown step has its own
deadline so neither can eat the grace period.

A failed join_call also escaped as an unhandled rejection with a full
stack, because revoice awaits it inside an async executor it never
guards; the error is already latched and reported, so that call no longer
settles at all.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This commit is contained in:
Leonid Pershin
2026-09-09 02:44:36 +03:00
co-authored by Claude Opus 5
parent 224d5679a3
commit c7f2080604
3 changed files with 33 additions and 10 deletions
+5
View File
@@ -108,6 +108,11 @@ export function createRevoice(token: string, baseURL: string, node: string): Rev
return await post(path, payload, params);
} catch (err) {
pendingError = describeApiError(err);
// revoice awaits this inside an async executor it never guards, so a
// rejection here escapes as an unhandled rejection with a full stack for
// what is an ordinary, already-reported error. Our join() wrapper below
// picks the latched error up, so this call simply never settles.
if (path.endsWith("/join_call")) return new Promise<never>(() => {});
throw pendingError;
}
};