fix(gateway): clean up Discord typing tasks on teardown - #23083
fix(gateway): clean up Discord typing tasks on teardown#23083nazirulhafiy wants to merge 1 commit into
Conversation
liuhao1024
left a comment
There was a problem hiding this comment.
This PR includes files that don't belong in the repository:
-
Personal Obsidian notes from an iCloud path:
"/Users/hafiy/Library/Mobile Documents/iCloud~md~obsidian/Documents/My Second Brain/Resources/Raw/2026-04-27 - signül on X.md"— this is a personal note about a tweet, not related to the hermes-agent codebase. -
.bakfiles:gateway/platforms/discord.py.bak,gateway/run.py.bak,hermes_cli/commands.py.bak— backup files should not be committed. -
docs/plans/2026-05-05-caveman-enforcement-plan.md— an internal implementation plan document that looks like it was accidentally staged.
Please remove these files from the PR and only include the actual code changes for the Discord typing indicator fix.
6ddeb8c to
a4719f2
Compare
Thank you. There were mistakes in submission and working to rectify. |
74a4b1a to
1e9bb6b
Compare
fd92bfa to
8412058
Compare
19e199c to
38dc93a
Compare
|
Hermes autofix diagnosed the failing Contributor Attribution Check: the PR commit author email I prepared the one-line fix and verified it locally:
I could not push directly to the contributor fork branch ( |
d066073 to
125c2ed
Compare
125c2ed to
f60ec3f
Compare
The persistent per-channel typing loops spawned by send_typing() are not tracked by BasePlatformAdapter's message-processing task registry, so on gateway restart, exception path, or signal handler, they were never cancelled. The typing endpoint's own 10-second timeout eventually cleared the indicator on Discord's side, but Hermes would hang in a "typing..." state for at least that long after every shutdown, with no way to abort early. This drains the entire _typing_tasks dict from two teardown points: 1. disconnect() — explicit operator-initiated close. Awaits _cancel_typing_tasks before tearing down voice clients and the discord.py client, so no in-flight loop survives the close. 2. cancel_background_tasks() — the base adapter's shutdown hook called from gateway/run.py on global shutdown. Drains Discord-specific tasks first, then defers to super() so the base cleanup still runs. _cancel_typing_tasks() snapshots the dict, clears it immediately, cancels each not-done task, and waits up to 2.0s for them to acknowledge the cancel via asyncio.gather(..., return_exceptions=True). Anything still alive at 2s logs a warning rather than blocking shutdown indefinitely — the typing indicator on Discord's side will clear on its own after ~10s even if the bot process is gone. Resubmission of NousResearch#23083 (auto-closed 2026-05-27 due to personal files in the diff). The original 1-file +39-line patch is preserved verbatim in spirit; only the file path changed because commit cc8e5ec (2026-05-12, pre-closure) migrated the Discord adapter from gateway/platforms/discord.py to plugins/platforms/discord/adapter.py as a bundled plugin with full Teams parity. The bug surface and fix logic are identical to the original. Repro path (from the original PR's context): on a healthy gateway, run /ask in 2+ Discord DMs back-to-back. Force-kill the gateway process (kill -9) or send SIGTERM. The "Hermes is typing..." indicator in any chat that had not yet hit its stop_typing() call will linger for ~10s after the bot process is gone. With this patch, _cancel_typing_tasks runs from disconnect() and from the shutdown handler before the process exits, so Discord sees the typing loop end promptly.
The persistent per-channel typing loops spawned by send_typing() are not tracked by BasePlatformAdapter's message-processing task registry, so on gateway restart, exception path, or signal handler, they were never cancelled. The typing endpoint's own 10-second timeout eventually cleared the indicator on Discord's side, but Hermes would hang in a "typing..." state for at least that long after every shutdown, with no way to abort early. This drains the entire _typing_tasks dict from two teardown points: 1. disconnect() — explicit operator-initiated close. Awaits _cancel_typing_tasks before tearing down voice clients and the discord.py client, so no in-flight loop survives the close. 2. cancel_background_tasks() — the base adapter's shutdown hook called from gateway/run.py on global shutdown. Drains Discord-specific tasks first, then defers to super() so the base cleanup still runs. _cancel_typing_tasks() snapshots the dict, clears it immediately, cancels each not-done task, and waits up to 2.0s for them to acknowledge the cancel via asyncio.gather(..., return_exceptions=True). Anything still alive at 2s logs a warning rather than blocking shutdown indefinitely — the typing indicator on Discord's side will clear on its own after ~10s even if the bot process is gone. Resubmission of NousResearch#23083 (auto-closed 2026-05-27 due to personal files in the diff). The original 1-file +39-line patch is preserved verbatim in spirit; only the file path changed because commit cc8e5ec (2026-05-12, pre-closure) migrated the Discord adapter from gateway/platforms/discord.py to plugins/platforms/discord/adapter.py as a bundled plugin with full Teams parity. The bug surface and fix logic are identical to the original. Repro path (from the original PR's context): on a healthy gateway, run /ask in 2+ Discord DMs back-to-back. Force-kill the gateway process (kill -9) or send SIGTERM. The "Hermes is typing..." indicator in any chat that had not yet hit its stop_typing() call will linger for ~10s after the bot process is gone. With this patch, _cancel_typing_tasks runs from disconnect() and from the shutdown handler before the process exits, so Discord sees the typing loop end promptly.
Summary
Fixes a Discord gateway teardown leak where typing indicator background tasks could survive
disconnect()/cancel_background_tasks(), interfering with clean shutdown during gateway restarts or profile switches.Changes
disconnect()cancels typing tasks before shutting down.cancel_background_tasks()clears Discord typing tasks before delegating to the base cleanup path.Validation
python -m py_compile gateway/platforms/discord.pyNotes
This PR is intentionally scoped to
gateway/platforms/discord.pyonly.