Skip to content

fix(gateway): prevent Discord disconnects from blocking event loop - #56212

Merged
teknium1 merged 1 commit into
mainfrom
hermes/hermes-160bc820
Jul 1, 2026
Merged

teknium1 merged 1 commit into
mainfrom
hermes/hermes-160bc820

Conversation

@teknium1

@teknium1 teknium1 commented Jul 1, 2026

Copy link
Copy Markdown
Collaborator

Summary

The Discord gateway no longer disconnects with ClientConnectionResetError when the agent resolves model metadata during message handling.

Root cause: agent/models_dev.py fetches the models.dev registry with a synchronous requests.get(timeout=15). Called from the async gateway handlers, that call blocked the single-threaded asyncio event loop for up to 15s — starving Discord heartbeats and triggering "Cannot write to closing transport" disconnects.

Changes

  • agent/model_metadata.py: add get_model_context_length_async() — offloads the entire existing sync resolution chain to a worker thread via asyncio.to_thread(). The sync path stays the single source of truth for the models.dev cache.
  • gateway/run.py: switch the two async call sites (_prepare_inbound_message_text, _handle_message_with_agent) to await get_model_context_length_async(...). The third call site (_format_session_info) is a plain sync def and is correctly left untouched.

Salvage note

Salvaged from @itenev's PR #22753 with authorship preserved. Dropped the original PR's fetch_models_dev_async / lookup_models_dev_context_async aiohttp variants (86 LOC): they had zero callers (dead code) and had drifted from the sync cache logic (missing the fresh-by-mtime disk-cache short-circuit). The to_thread wrapper already runs the sync path off-loop, so the parallel aiohttp implementation was redundant. Net: 119/-4 → 34/-4.

Validation

E2E: cold-cache resolution (0.49s real network fetch through the sync chain) while a 50ms heartbeat task ran concurrently.

Before (on-loop) After (to_thread)
Max event-loop gap ~490ms (loop frozen) 52ms (responsive)
Discord heartbeat starved steady
Context resolved 1,000,000 (int, correct)

Tests: tests/agent/test_models_dev.py + tests/agent/test_model_metadata.py — 137 passed.

Infographic

PR #22753 infographic

models_dev.py's fetch uses a synchronous requests.get(timeout=15). Called
from the async gateway message handlers, it blocked the event loop for up
to 15s, starving Discord heartbeats and causing ClientConnectionResetError
disconnects.

Adds get_model_context_length_async() which offloads the entire sync
resolution chain to a worker thread via asyncio.to_thread(), and switches
the two async gateway call sites (_prepare_inbound_message_text,
_handle_message_with_agent) to await it. The loop stays responsive; the
sync path remains the single source of truth for the cache.

Salvaged from PR #22753 by @itenev. Follow-up: dropped the unused
fetch_models_dev_async/lookup_models_dev_context_async aiohttp variants
from the original PR (dead code with zero callers that had drifted from
the sync cache logic) — the to_thread wrapper already runs the sync path
off-loop, so they were redundant.
@teknium1
teknium1 merged commit f981d47 into main Jul 1, 2026
31 checks passed
@teknium1
teknium1 deleted the hermes/hermes-160bc820 branch July 1, 2026 09:17
@alt-glitch alt-glitch added type/bug Something isn't working comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery platform/discord Discord bot adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages P1 High — major feature broken, no workaround labels Jul 1, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/agent Core agent runtime: loop, agent_init, prompt builder, context-compression, responses endpoint comp/gateway Gateway runner, session dispatch, delivery P1 High — major feature broken, no workaround platform/discord Discord bot adapter sweeper:risk-message-delivery Sweeper risk: may drop, duplicate, misroute, or suppress messages type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants