fix(rust): answer the request id when a handler panics - #2311
Open
rinceyuan wants to merge 1 commit into
Open
Conversation
Requests are dispatched to their own spawned tasks, so a panic inside a handler is isolated by tokio and the request is simply never answered - the caller waits out its own timeout. Catch the unwind at the dispatch boundary and reply with a JSON-RPC internal error (-32603) instead; the panic payload is not exposed. Also corrects the stop_event_loop, Drop and lifecycle-test documentation, which still claimed in-flight handlers complete before the loop exits. That stopped being true when request dispatch moved to spawned tasks. Fixes github#2053.
Contributor
There was a problem hiding this comment.
Pull request overview
Adds panic recovery at Rust JSON-RPC request dispatch, ensuring callers receive an internal-error response while preserving event-loop isolation.
Changes:
- Converts handler panics into JSON-RPC
-32603responses. - Updates lifecycle documentation for detached handlers.
- Adds regression coverage for panic recovery and continued request handling.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated no comments.
| File | Description |
|---|---|
rust/src/session.rs |
Catches dispatch panics and clarifies shutdown behavior. |
rust/tests/session_test.rs |
Tests panic responses and updates lifecycle commentary. |
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
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.
Summary
Follow-up items from #2034, tracked in #2053.
1. A panicking request handler left the request unanswered.
Since #2034 each inbound JSON-RPC request is dispatched to its own
tokio::spawned task. A panic inside a handler is isolated to that task, so the request id is simply never answered and the caller waits out its own timeout. This catches the unwind at the dispatch boundary and replies with-32603for that id. The panic payload is not exposed; the method name is logged aterror!.2. Stale lifecycle documentation.
Session::stop_event_loopand theDropimpl still claimed that in-flight handlers complete before the loop exits. That has not been true since request dispatch moved to spawned tasks. The rustdoc now says handlers may outlive the loop and that their response can be lost if the connection closes first. The comment onstop_event_loop_completes_in_flight_handlerclaimed the loop was parked insidehandle_request, which is likewise no longer how the test passes.Tests
New
panicking_request_handler_responds_with_internal_errorinsession_test:userInput.requestinto a handler that panics and asserts a-32603response for that id,Verified it times out (the reported symptom) with the source change reverted.
The
e2etarget andcli_resolution_test::stale_env_override_falls_throughneed the bundled CLI, which cannot be downloaded from this machine (TLS interception onregistry.npmjs.org), so they were run withCOPILOT_SKIP_CLI_DOWNLOAD=1and fail identically on a clean checkout.Fixes #2053.