From 95e2d4472bf6084ab870dccbf2c963a2cdf91765 Mon Sep 17 00:00:00 2001 From: EJClaw Date: Tue, 18 Aug 2026 22:05:57 +0900 Subject: [PATCH] fix(bot): tolerate slow DAVE join + never crash on a receive-stream error - Raise the voice-Ready ceiling 20s->40s: the DAVE/MLS handshake cycles signalling<->connecting and can take ~25s, so 20s spuriously failed the join. - Handle AudioReceiveStream 'error' (e.g. a DAVE decrypt/UDP GenericFailure on one packet): log and free the speaker slot instead of letting the unhandled 'error' event crash the whole bot process. Co-Authored-By: Claude Opus 4.7 --- dave/bot.mjs | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/dave/bot.mjs b/dave/bot.mjs index e256688..4932055 100644 --- a/dave/bot.mjs +++ b/dave/bot.mjs @@ -167,9 +167,11 @@ client.once('clientReady', async () => { }); try { - await entersState(connection, VoiceConnectionStatus.Ready, 20_000); + // The DAVE/MLS handshake here cycles signalling<->connecting several times + // and can take ~25s, so give it a generous ceiling before declaring failure. + await entersState(connection, VoiceConnectionStatus.Ready, 40_000); } catch (e) { - log(`FATAL: voice connection did not become Ready in 20s (${e.message})`); + log(`FATAL: voice connection did not become Ready in 40s (${e.message})`); return leaveAndExit(1); } log(`✅ JOINED & READY. channel=${CHANNEL_ID} — staying connected, listening for speakers…`); @@ -196,6 +198,10 @@ client.once('clientReady', async () => { const decoder = new prism.opus.Decoder({ rate: 48000, channels: 2, frameSize: 960 }); const chunks = []; opusStream.on('data', () => { perUser.get(userId).opusPackets++; }); + // A receive-stream error (e.g. a DAVE decrypt/UDP GenericFailure on one + // packet) must NOT crash the process — log it and free the slot so the + // next utterance still works. + opusStream.on('error', (e) => { log(`recv stream error user=${userId}: ${e.message}`); active.delete(userId); }); opusStream.pipe(decoder); decoder.on('data', (d) => { chunks.push(d);