cherry-pick: fix(mcp): move t.cancel() inside try block (#60213)#3
Merged
Conversation
… /exit
During shutdown, the event loop is closed before MCP tasks finish, so
t.cancel() → call_soon() raises RuntimeError('Event loop is closed').
Moved t.cancel() inside the try block at both locations (keepalive
loop and _wait_for_reconnect_or_shutdown).
Closes NousResearch#60197
(cherry picked from commit 8c7afb4)
Co-authored-by: AIalliAI <285906080+AIalliAI@users.noreply.github.com>
Owner
Author
Review: APPROVE (submitted as comment - unable to approve own PR)Verification
AnalysisThe fix correctly addresses Applied at both cleanup locations in the MCP tool lifecycle management, ensuring consistent behavior. Recommendation: MERGE |
AIalliAI
commented
Jul 7, 2026
AIalliAI
left a comment
Owner
Author
There was a problem hiding this comment.
Hermes Agent Code Review
Verdict: APPROVED (note: cannot self-approve — submitted as comment)
Correctness
- Moving
t.cancel()inside the try block is the right fix: when the event loop is already closed (shutdown via/exit),t.cancel()raisesRuntimeError("Event loop is closed"). Catching it prevents the crash reported in NousResearch#60197. - Applied consistently at both cleanup locations (
_wait_for_lifecycle_eventand_wait_for_reconnect_or_shutdown).
Edge Cases
- Task not cancelled when loop is closed: If
t.cancel()raisesRuntimeError, we skipawait t— but the loop is already closed, so there is nothing to clean up. Acceptable. await talso raising RuntimeError: Now explicitly caught (was previously caught only by the broadExceptionhandler), which is an improvement.- Other exceptions from
t.cancel(): Caught by theexcept Exceptionfallback, identical to the original behavior.
Code Quality
- Splitting
except (CancelledError, RuntimeError)fromexcept Exceptionimproves readability and documents intent. - The
passpattern in both handlers is appropriate for afinallycleanup block — these are best-effort cleanup operations during teardown. - Minimal diff (8 insertions, 4 deletions) — no unnecessary changes.
Cherry-Pick Hygiene
- Author preserved:
webtecnica - Commit message follows conventional format
- Cleanly cherry-picked from upstream NousResearch#60213
Minor Nit (non-blocking)
- The
except (asyncio.CancelledError, RuntimeError): passis technically redundant — theexcept Exception: passbelow would catch both anyway. But the explicit listing serves as documentation that these are the expected exceptions during shutdown.
Reviewed by Hermes Agent
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Cherry-pick of PR NousResearch#60213 from NousResearch/hermes-agent.
Original PR
Changes
Moves
t.cancel()insidetryblock at both cleanup locations intools/mcp_tool.py:_wait_for_lifecycle_eventmethod_wait_for_reconnect_or_shutdownmethodThis prevents
RuntimeError('Event loop is closed')during shutdown when the event loop closes before MCP tasks finish cleanup.Verification