Skip to content

fix(discord): re-arm voice inactivity timer on user input - #106004

Closed
gaoanze888 wants to merge 1 commit into
NousResearch:mainfrom
gaoanze888:fix-105974-voice-inactivity-rearm
Closed

gaoanze888 wants to merge 1 commit into
NousResearch:mainfrom
gaoanze888:fix-105974-voice-inactivity-rearm

Conversation

@gaoanze888

@gaoanze888 gaoanze888 commented Sep 8, 2026

Copy link
Copy Markdown
Contributor

What does this PR do?

Fixes a silent mid-conversation disconnect: when the bot joins a Discord voice channel, the inactivity auto-leave timer is armed once. After that, user voice input does not reset the timer — so in an active two-way voice conversation the bot silently disconnects exactly voice_channel_inactivity_timeout_seconds (default 300) after joining, even if the user is talking the whole time. This PR re-arms the timer on a successful user transcript.

Related Issue

Closes #105974.

Type of Change

  • 🐛 Bug fix (non-breaking change which fixes an issue)

Changes Made

DiscordAdapter._reset_voice_timeout() was called from the playback paths (join_voice_channel, play_in_voice_channel's finally, play_ack_in_voice) but not from _process_voice_input() (the STT path that actually hears the user). The only thing that kept an auto-joined bot alive was its own TTS playback.

The fix: call _reset_voice_timeout(guild_id) in _process_voice_input() once a valid (non-hallucinated) transcript is detected, mirroring the playback-path re-arm pattern. Additive — one line plus a comment.

Why this is safe:

  • The re-arm is gated on a successful transcript — a silent channel still times out (verified by a negative test).
  • Compatible with fix(discord): voice inactivity timeout fires under /voice off, spamming the channel #43165's /voice off fix: _voice_timeout_handler returns early when mode == "off", so re-arming there is a no-op (no spam, no disconnect).
  • _reset_voice_timeout cancels the existing task before re-arming, so calling it on every utterance does not accumulate timers.

How to Test

New regression tests (tests/gateway/test_discord_voice_timeout_rearm.py):

  • test_process_voice_input_re_arms_inactivity_timeout — valid transcript → _reset_voice_timeout called with the guild id. Fails without the fix (mutation-verified).
  • test_process_voice_input_does_not_rearm_on_failed_stt — failed STT → _reset_voice_timeout NOT called (the re-arm is correctly gated).

Both pass with the fix; the positive test fails when the fix is reverted.

Checklist

Code

Documentation & Housekeeping

  • N/A — additive one-line re-arm reusing existing _reset_voice_timeout; no public API or doc surface changed

…ch#105974)

Closes NousResearch#105974. The bot's own TTS playback re-arms the
auto-disconnect inactivity timer via play_in_voice_channel's finally,
but _process_voice_input (the STT path that hears the user) never
called _reset_voice_timeout. In an active two-way voice conversation
the bot silently disconnected exactly
voice_channel_inactivity_timeout_seconds after joining, even if the
user talked the whole time.

- Call _reset_voice_timeout(guild_id) in _process_voice_input once a
  valid (non-hallucinated) transcript is detected, mirroring the
  playback-path re-arm pattern.

The re-arm is gated on a successful transcript, so a silent channel
still times out. Compatible with NousResearch#43165's /voice off fix: the handler
returns early when mode==off, so re-arming there is a no-op.

Tests: assert _process_voice_input re-arms on valid input (fails
without the fix) and does NOT re-arm on failed STT. Mutation-verified.
@alt-glitch alt-glitch added type/bug Something isn't working P3 Low — cosmetic, nice to have comp/plugins Plugin system and bundled plugins platform/discord Discord bot adapter labels Sep 8, 2026
@alt-glitch

Copy link
Copy Markdown
Contributor

This was generated by AI during triage.

Related: #42597 (open) also re-arms the voice inactivity timer from inbound user audio as part of a broader voice-responsiveness change; this PR is the narrow one-line variant for #105974. Reviewers may prefer one or the other.

@gaoanze888

Copy link
Copy Markdown
Contributor Author

Re: the triage note about #42597 — noting the relationship so it's easy to triage.

#42597 ("keep voice meetings responsive", opened June, last updated July, mergeable: false / dirty) is a broader Discord-voice redesign: it resets inactivity timers from inbound audio and reworks the transcript path, makes /voice join default to meeting mode, and adds a HERMES_DISCORD_VOICE_TRANSCRIPT_AGENT_TURNS opt-in. It's three months stale with conflicts.

This PR is the focused fix for #105974 (re-arm the voice inactivity timer on user input) — same one behavior, minimal scope, 2 new mutation-verified tests, no surrounding redesign. If a maintainer prefers to land the narrow fix first and let the broader redesign follow separately, this is ready; if they'd rather wait on #42597, that's fine too.

@gaoanze888

Copy link
Copy Markdown
Contributor Author

Closing after a maintainer-style re-audit disproved this PR's root-cause premise.

The PR parent already resets the inactivity timer in the real receive path:

for user_id, pcm_data in completed:
    if not self._is_allowed_user(...):
        continue
    self._reset_voice_timeout(guild_id)
    await self._process_voice_input(...)

The issue log confirms that reset fired: join was at 13:47:37, the accepted voice input was at 13:47:56, and disconnect was at 13:52:55 — approximately 300 seconds after the input, not 300 seconds after join.

The added _process_voice_input() reset therefore only performs a second cancel/re-arm after successful STT. The tests called _process_voice_input() directly and bypassed _voice_listen_loop(), so they proved that duplicate implementation detail rather than the production lifecycle. The negative test was also inconsistent with production: an allowed completed utterance already resets the timer before STT, including when STT later fails.

The actual unresolved signal in the report is that no further completed utterances / Voice input from user logs appeared after the first response while the user continued speaking. Code inside _process_voice_input() cannot run once utterance production has stalled, so this PR cannot fix that failure.

Thanks to @liuhao1024 for pointing out the timestamp evidence and existing caller reset. I should have traced the full caller lifecycle before opening this; closing to avoid consuming maintainer review time on a redundant fix. The receiver/transcription stall should be investigated separately with a focused reproduction.

@gaoanze888 gaoanze888 closed this Sep 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/plugins Plugin system and bundled plugins P3 Low — cosmetic, nice to have platform/discord Discord bot adapter type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Discord voice: inactivity timer never re-arms on user voice input — bot drops mid-conversation (auto-join)

2 participants