Skip to content

fix(qqbot): prevent busy-loop when WebSocket closes without exception - #27821

Closed
ygd58 wants to merge 1 commit into
NousResearch:mainfrom
ygd58:fix/qqbot-websocket-busy-loop
Closed

fix(qqbot): prevent busy-loop when WebSocket closes without exception#27821
ygd58 wants to merge 1 commit into
NousResearch:mainfrom
ygd58:fix/qqbot-websocket-busy-loop

Conversation

@ygd58

@ygd58 ygd58 commented May 18, 2026

Copy link
Copy Markdown
Contributor

Problem

When ws.closed becomes True, _read_events() exits its while loop cleanly without raising an exception. _listen_loop() immediately starts a new iteration with no sleep or reconnect delay, spinning at 100% CPU.

Fix

Two guards in _read_events():

  1. Pre-check: raise if ws is already closed on entry
  2. Post-check: raise after loop exits cleanly with ws.closed=True

Both surface as RuntimeError so _listen_loop schedules proper reconnect with exponential backoff.

Fixes #27810

@ygd58

ygd58 commented May 18, 2026

Copy link
Copy Markdown
Contributor Author

Checked #17704 and #20994:

This PR (#27821) adds both pre-check AND post-check (raise after while loop exits cleanly with ws.closed=True), which is the missing piece in both. The post-check is what actually prevents the busy-loop in the 4009 session timeout scenario reported in #27810.

@BoardJames-Bot

Copy link
Copy Markdown

CI triage: the two failing checks are not from the qqbot WebSocket change itself.

  • test fails only in tests/hermes_cli/test_aux_config.py because the PR's merged checkout is missing the current DEFAULT_CONFIG["auxiliary"]["session_search"] block while _AUX_TASKS references session_search.
  • Scan PR for critical supply chain risks fires because the PR diff against current main includes hermes_cli/setup.py as an install-hook file (it appears as deleting the newer xAI OAuth setup changes from main). The workflow tried to post its detailed comment but fork PR tokens are read-only: Resource not accessible by integration (addComment).

Root cause: branch is stale relative to current main, not a qqbot regression. Next owner action: rebase/merge current origin/main into fix/qqbot-websocket-busy-loop and push the fork branch; that should remove the unrelated hermes_cli/setup.py/aux-config drift and re-run CI.

When ws.closed becomes True, _read_events() exits its while loop
cleanly without raising an exception. _listen_loop() then immediately
starts a new iteration with no sleep or reconnect delay, spinning at
100% CPU (issue NousResearch#27810).

Fix: two guards in _read_events():
1. Pre-check: raise immediately if ws is already closed on entry
2. Post-check: raise after loop exits cleanly with ws.closed=True

Both cases surface as RuntimeError so _listen_loop's exception handler
schedules a proper reconnect with exponential backoff instead of
spinning.

Fixes NousResearch#27810
@ygd58
ygd58 force-pushed the fix/qqbot-websocket-busy-loop branch from 19b332a to e5863ee Compare May 18, 2026 07:01
@alt-glitch alt-glitch added type/bug Something isn't working P2 Medium — degraded but workaround exists platform/qqbot QQ Bot adapter comp/gateway Gateway runner, session dispatch, delivery labels May 18, 2026
@BoardJames-Bot

Copy link
Copy Markdown

BoardJames CI triage: the test failure is the same merged-state aux-config drift I reproduced on current main, not caused by this PR's changes. tests/hermes_cli/test_aux_config.py expects auxiliary.session_search, but session_search no longer uses an auxiliary LLM and current config removed that block while leaving the picker/test stale. Opened #27835 to remove session_search from the auxiliary model picker and update the test; focused validation there: python -m pytest tests/hermes_cli/test_aux_config.py -q -o 'addopts=' -> 21 passed.

@teknium1

Copy link
Copy Markdown
Contributor

Thanks for the QQBot reconnect fix. This specific busy-loop issue has since been fixed on current main by an automated hermes-sweeper review.

Evidence:

  • gateway/platforms/qqbot/adapter.py:684 now raises RuntimeError when _read_events() is entered with a closed-but-non-None WebSocket, instead of returning normally forever.
  • gateway/platforms/qqbot/adapter.py:639 routes that RuntimeError through the reconnect/backoff path in _listen_loop().
  • tests/gateway/test_qqbot.py:2201 adds a regression test for the closed-but-non-None WebSocket case.
  • The fix landed in 3eeca4613d618618093db416b564a2b9ef8dbe6a (fix(qqbot): stop 100% CPU spin when WebSocket is closed but not None (#31193, #31771) (#40574)).

Closing as implemented on main.

@teknium1 teknium1 closed this Jun 13, 2026
@teknium1 teknium1 added the sweeper:implemented-on-main Sweeper: behavior already present on current main label Jun 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

comp/gateway Gateway runner, session dispatch, delivery P2 Medium — degraded but workaround exists platform/qqbot QQ Bot adapter sweeper:implemented-on-main Sweeper: behavior already present on current main type/bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[Bug]: QQBot closed WebSocket 导致无限空循环,单线程 CPU 100%

4 participants