fix: never mint a request id already used by a completed caller-supplied id#3127
Open
ayaangazali wants to merge 1 commit into
Open
fix: never mint a request id already used by a completed caller-supplied id#3127ayaangazali wants to merge 1 commit into
ayaangazali wants to merge 1 commit into
Conversation
…ied id The mint loops in JSONRPCDispatcher.send_raw_request and DirectDispatcher._dispatch_request only skipped ids still in flight, so once a caller-supplied integer (or numeric-string) id completed, the monotonic counter could reach the same value and reuse it on the wire. The spec forbids reusing a request id within a session. Accepting a supplied id now advances the mint counter past its coerced key, which makes the skip loops provably unreachable, so they are removed.
ayaangazali
force-pushed
the
fix/minted-request-id-reuse
branch
from
July 18, 2026 11:30
79af94f to
64bfa61
Compare
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
Fixes #3126.
When a caller supplies its own request id via
CallOptions["request_id"], the dispatcher's minted-id sequence could later land on that same id once the supplied request completed, sending two different requests under one id in the same session. The spec says the requestor must never reuse a request id within a session, and the SDK pins the same rule asprotocol:request-id:uniqueintests/interaction/_requirements.py.Root cause
Both mint loops only skipped ids still in flight:
The comment right above ("Mint past any key a supplied id occupies") shows the intent, but the guard stopped holding the moment the supplied request completed. Since
coerce_request_idfolds"2"and2into one key, numeric-string ids hit this too.DirectDispatcherhad the same pattern with_in_flight_ids(discarded in afinally).Fix
Accepting a supplied id now advances the mint counter past its coerced integer key:
That makes the old skip loops provably unreachable (the counter is always past every supplied integer key and every previously minted id), so they are removed rather than left as dead branches the coverage gate would flag.
Two deliberate scope notes:
test_send_raw_request_with_in_flight_request_id_raises_and_frees_id_on_completionand this PR does not touch it. Only the dispatcher's own minting is constrained.test_minted_ids_skip_a_caller_supplied_id_still_in_flightwent from[1, 2, 4]to[4, 5, 6]). Ids are opaque per spec, so I did not treat this as a breaking change needing a migration entry; happy to add one if you see it differently.Tests
test_minted_ids_never_reuse_a_completed_supplied_id, parametrized over both dispatchers and both id spellings (2,"2"). Fails on current main with wire ids[2, 1, 2]; passes with the fix ([2, 3, 4]).test_minted_ids_skip_a_caller_supplied_id_still_in_flight.CallOptions.request_iddocstring extended with the new guarantee.Ran locally:
AI disclosure, per CONTRIBUTING: Claude Code helped me find this and sanity-check the invariant reasoning; I wrote up the repro, ran everything locally, and I can defend each line here in my own words. First PR to this repo and I'm a freshman still learning the ropes, so if the counter-advance approach clashes with something I haven't seen, please say so and I'll rework it.