Skip to content

fix(client): abort legacy SSE reconnect chain when the originating request times out - #2616

Open
claude[bot] wants to merge 17 commits into
mainfrom
fix/2615-legacy-sse-reconnect-after-timeout
Open

fix(client): abort legacy SSE reconnect chain when the originating request times out#2616
claude[bot] wants to merge 17 commits into
mainfrom
fix/2615-legacy-sse-reconnect-after-timeout

fix(client): cover all settlement paths for the request-scoped abort;…

29c4e86
Select commit
Loading
Failed to load commit list.
Claude / Claude Code Review completed Aug 6, 2026 in 24m 51s

Code review found 3 important issues

Found 4 candidates, confirmed 6. See review comments for details.

Details

Severity Count
🔴 Important 3
🟡 Nit 3
🟣 Pre-existing 0
Severity File:Line Issue
🔴 Important packages/core-internal/src/shared/protocol.ts:1421-1428 resumptionToken short-circuit: unguarded onerror on request-scoped abort; drops onresumptiontoken/onRequestStreamEnd
🟡 Nit .changeset/legacy-sse-reconnect-after-timeout.md:1-5 Changeset overclaims: wire-visible cancellation IS changed for modern-era maxTotalTimeout settlements
🟡 Nit docs/migration/support-2026-07-28.md:236-246 upgrade-to-v2.md still says requestSignal matters only for 2026-era cancellation — fourth stale doc site

Annotations

Check failure on line 1428 in packages/core-internal/src/shared/protocol.ts

See this annotation in the file changed.

@claude claude / Claude Code Review

resumptionToken short-circuit: unguarded onerror on request-scoped abort; drops onresumptiontoken/onRequestStreamEnd

On the legacy era this PR newly threads `requestAbort.signal` into the transport's resumptionToken short-circuit (`_send` in `packages/client/src/client/streamableHttp.ts`, ~line 957), but that path's outer `.catch(error => this.onerror?.(error))` has no intentional-abort guard — so a timeout/abort landing while the resume GET is in flight surfaces a spurious `AbortError` through `client.onerror`, contradicting the "no spurious onerror" contract this diff's comments and docs state. A one-line gu

Check warning on line 5 in .changeset/legacy-sse-reconnect-after-timeout.md

See this annotation in the file changed.

@claude claude / Claude Code Review

Changeset overclaims: wire-visible cancellation IS changed for modern-era maxTotalTimeout settlements

The changeset's claim that "the wire-visible cancellation behavior is unchanged for every (era × transport) combination" overclaims: the new `.finally()` `requestAbort?.abort()` makes modern-era (2026-07-28) `maxTotalTimeout` settlements close the per-request stream — which on that era IS the spec cancellation signal — where pre-PR nothing was put on the wire on that path (and successful completions now actively close the stream too). The behavior change is desirable, but the sentence should be 

Check warning on line 246 in docs/migration/support-2026-07-28.md

See this annotation in the file changed.

@claude claude / Claude Code Review

upgrade-to-v2.md still says requestSignal matters only for 2026-era cancellation — fourth stale doc site

The doc-update round in 29c4e86 fixed three of the four prose sites describing the old 2026-only `requestSignal` contract, but a fourth survives: the "Transport interface contract" bullet in `docs/migration/upgrade-to-v2.md` (~lines 1823-1832) still says `hasPerRequestStream` / `requestSignal` / `onRequestStreamEnd` "matter only for 2026-era per-request-stream cancellation and Mcp-Param-* header attachment" — after this PR the signal is threaded and aborted on every settlement at either era. Sam