[v1.x] fix: guard respond() and cancel() against concurrent cancellation race (#2416)#3088
Open
shivamsingh-007 wants to merge 1 commit into
Open
Conversation
When a CancelledNotification arrives after the handler has returned but before respond() is called, cancel() sets _completed=True and sends an error response. respond() then hits assert not self._completed and crashes the server. Replace the assert with a silent early return in respond(), and add the same guard to cancel() so duplicate completion is harmless either way. Either path wins exactly once under cooperative multitasking. Fixes modelcontextprotocol#2416 Signed-off-by: Shivam Singh <shivam.singh.koyad2006@gmail.com> Github-Issue: modelcontextprotocol#2416
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
When a client sends
otifications/cancelled\ after the handler has returned
but before
espond()\ is called, the server crashes with
\AssertionError: Request already responded to.
Related Issue
Fixes #2416
What changed
a silent early return, so a concurrent cancellation that already errored
is not double-replied to.
so the reverse ordering (respond then cancel) is also safe.
Why this approach
The root cause is a race window between handler return and
espond().
There is no \�wait\ checkpoint in that gap, so \CancelledError\ cannot be
delivered to the handler task. The fix makes
espond()\ idempotent:
whichever path (\cancel()\ via the notification handler or
espond()\ via
_handle_request) sets _completed\ first wins; the other path silently
no-ops. This is safe under cooperative multitasking since _completed\ is
set synchronously before the first \�wait\ in each method.
Testing done
and asserts no exception is raised
Checklist