hermes - 💡(How to fix) Fix Discord typing indicator stuck indefinitely due to race condition in _keep_typing cleanup [1 pull requests]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

Error Message

async def send_typing(self, chat_id: str, metadata=None) -> None: if not self._client: return route = discord.http.Route("POST", "/channels/{channel_id}/typing", channel_id=chat_id) try: await self._client.http.request(route) except Exception: pass

Root Cause

The bug is caused by a race condition between two independent typing refresh loops:

  1. _keep_typing (gateway/platforms/base.py): Runs every 2s, calling send_typing().
  2. _typing_loop (gateway/platforms/discord.py): Runs every 8s, calling POST /channels/{id}/typing.

Fix Action

Fixed

Code Example

async def send_typing(self, chat_id: str, metadata=None) -> None:
    if not self._client: return
    route = discord.http.Route("POST", "/channels/{channel_id}/typing", channel_id=chat_id)
    try:
        await self._client.http.request(route)
    except Exception: pass
RAW_BUFFERClick to expand / collapse

Describe the Bug

The Discord bot typing indicator ("hm-macmini is typing...") sometimes remains stuck indefinitely, even after the agent has finished responding and sent the message. This occurs in Discord threads.

Root Cause Analysis

The bug is caused by a race condition between two independent typing refresh loops:

  1. _keep_typing (gateway/platforms/base.py): Runs every 2s, calling send_typing().
  2. _typing_loop (gateway/platforms/discord.py): Runs every 8s, calling POST /channels/{id}/typing.

The Race Condition

  1. When a message is processed, _process_message_background (base.py) starts _keep_typing.
  2. _keep_typing calls send_typing(), which creates a persistent _typing_loop task in the Discord adapter.
  3. When the agent finishes, run.py calls stop_typing() to cancel the _typing_loop.
  4. However, _keep_typing continues running and re-triggers send_typing() every 2s.
  5. If stop_typing() cancels the loop just before _keep_typing calls send_typing() again, a new _typing_loop is created.
  6. The cleanup in _process_message_background's finally block (base.py line 3404-3412) attempts to stop typing, but uses a 0.5s timeout (_stop_typing_task). If the cancellation takes longer, it gives up.
  7. The _typing_loop escapes cancellation and continues refreshing the Discord typing indicator every 8s indefinitely.

Evidence from gateway.error.log shows Connection timeout errors to Discord API during this period, which can delay the cancellation path and increase the likelihood of the race condition.

Affected Files

  • gateway/platforms/base.py (Lines 2215-2295, 3036-3051, 3404-3412)
  • gateway/platforms/discord.py (Lines 2703-2747)

Proposed Fix

Remove the independent _typing_loop from discord.py and rely solely on _keep_typing for refreshes. Change discord.py:send_typing to a single, non-blocking HTTP request:

async def send_typing(self, chat_id: str, metadata=None) -> None:
    if not self._client: return
    route = discord.http.Route("POST", "/channels/{channel_id}/typing", channel_id=chat_id)
    try:
        await self._client.http.request(route)
    except Exception: pass

This eliminates the second loop, removing the race condition entirely. Discord's native 10s TTL will handle expiration when _keep_typing stops.

Reproduction Steps

  1. Send a long-running request to the bot in a Discord thread (e.g., 2+ minutes of tool calls).
  2. Interrupt the bot or wait for it to finish.
  3. Observe that the "typing..." indicator often remains stuck for hours until the gateway restarts.

Environment

  • Hermes Agent Version: v[Current]
  • OS: macOS
  • Platform: Discord

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING