Skip to content

fix: preserve abort signals in browser routing - #160

Merged
rgarcia merged 1 commit into
mainfrom
hypeship/preserve-routing-abort
Aug 10, 2026
Merged

fix: preserve abort signals in browser routing#160
rgarcia merged 1 commit into
mainfrom
hypeship/preserve-routing-abort

Conversation

@rgarcia

@rgarcia rgarcia commented Aug 8, 2026

Copy link
Copy Markdown
Contributor

summary

  • preserve the caller-provided abort signal when rewriting browser requests for direct VM routing
  • retain signals supplied through both RequestInit and Request inputs
  • add a deterministic regression test that fails with the previous dependent signal

why

Reconstructing a Request creates a dependent signal. Passing that derived signal to the routed fetch can intermittently leave streamed response reads blocked after the caller aborts.

tests

  • yarn test --runInBand
  • yarn lint
  • yarn build

Note

Medium Risk
Touches fetch routing for live SSE/telemetry streams where incorrect abort handling caused intermittent hangs; change is narrow but behavior-critical for cancellation.

Overview
Direct-to-VM browser routing no longer forwards the dependent AbortSignal from the reconstructed Request when issuing the routed innerFetch. buildRoutedInit now takes the original input and sets signal from originalInit?.signal, or from the caller’s Request when input is a Request, so aborts on allowlisted paths like telemetry/stream propagate to the underlying fetch and streamed reads can unwind after cancel.

A regression test asserts the routed fetch receives the same signal reference for both URL + RequestInit and Request call shapes.

Reviewed by Cursor Bugbot for commit 2eec02c. Bugbot is set up for automated code reviews on this repo. Configure here.

@rgarcia
rgarcia requested a review from Jayko001 August 8, 2026 14:10
@rgarcia
rgarcia merged commit c5ff0a5 into main Aug 10, 2026
11 checks passed
@rgarcia
rgarcia deleted the hypeship/preserve-routing-abort branch August 10, 2026 18:54
@Sayan-

Sayan- commented Aug 10, 2026

Copy link
Copy Markdown
Contributor

Post-merge verification. I reproduced the original failure independently and re-tested this change against it. The fix is correct.

What I verified on 2eec02c:

  • tsc --noEmit clean, full suite 405 passed. Reverting just the signal: line makes the new test fail (✕ preserves the caller abort signal for routed requests), so the guard is real rather than vacuous.
  • Real end-to-end A/B with a warm route cache and a forced GC, aborting an established SSE stream: 10/10 hung before this change, 0/10 after. Against a live streaming backend under three concurrent sessions cycling open-then-abort: 3/54 hung before, 0/60 after. A longer randomized run on the pre-fix code hung 37/170.
  • Choosing an identity assertion over a behavioral one was the right call. A hang-reproducing test needs --expose-gc, which jest.config.ts does not set, so it would have passed either way.

Two follow-ups:

  1. This also repairs stream.controller.abort(), the documented cancellation idiom, which was equally dead: 10/10 hung before, 0/10 after. Worth noting in the changelog, since callers who never passed a signal were affected too and may not realize this covers them.

  2. The Request-input branch preserves signal identity but not abort survival, so the summary line overstates it slightly. routingFetch(new Request(url, {signal})) still hangs 10/10 with this change applied, because input.signal is itself a dependent signal owned by the caller's Request; assign that Request to a variable first and it is 0/10. The test asserts routedSignal === request.signal, which passes either way, so it does not distinguish the two. Nothing to fix in the SDK, since fetchWithTimeout always passes a string URL and never reaches that branch, but the claim and the test both read stronger than the behavior.

Release status: 0.87.0 on npm does not contain this fix, so anyone hitting the original hang is still affected until #163 (0.87.1) merges.

@Sayan- Sayan- left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Verified post-merge, approving for the record.

@rgarcia

rgarcia commented Aug 10, 2026

Copy link
Copy Markdown
Contributor Author

addressed the follow-ups in #164:

  • the original test now says exactly what it proves: signal identity for RequestInit and Request inputs
  • added coverage that stream.controller.abort() retains the exact signal passed to the routed telemetry request
  • updated the 0.87.1 changelog to call out stream.controller.abort() cancellation

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants