Skip to content

fetch: abort the request when the response body reader is cancelled - #33231

Merged
Jarred-Sumner merged 10 commits into
mainfrom
farm/469333cb/fetch-reader-cancel-abort
Jul 4, 2026
Merged

fetch: abort the request when the response body reader is cancelled#33231
Jarred-Sumner merged 10 commits into
mainfrom
farm/469333cb/fetch-reader-cancel-abort

Conversation

@robobun

@robobun robobun commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

Fixes #33227 (reopen of #19211).

Repro

import { sleep } from "bun";

let cancelled = false;
let aborted = false;

const server = Bun.serve({
  fetch(request) {
    let count = 0;
    request.signal.addEventListener("abort", () => { aborted = true; });
    return new Response(new ReadableStream({
      async pull(controller) {
        controller.enqueue(`data: ${count++}\n\n`);
        await sleep(1000);
      },
      async cancel() { cancelled = true; },
    }), { headers: { "Content-Type": "text/event-stream" } });
  },
  port: 0,
});

const res = await fetch(`http://localhost:${server.port}`, { method: "POST" });
const reader = res.body!.getReader();
await reader.read();
await reader.read();

await reader.cancel();          // should abort the fetch / close the connection
await sleep(1000);

console.log("aborted   =", aborted, "(expected true)");
console.log("cancelled =", cancelled, "(expected true)");

Before: aborted = false, cancelled = false. The client kept the
connection open and drained the body, so the server never saw the cancel.

Cause

Cancelling a fetch response body reader (reader.cancel() /
res.body.cancel()) routes through the stream source's cancel handler to
FetchTasklet::on_stream_cancelled_callback, which called
ignore_remaining_response_body. That path sets the receive mode to Ignore
and calls enable_response_body_streaming(), which keeps reading and
discarding the remaining response body to leave the socket reusable. For an
unbounded or still-streaming response that drain never completes, so the
connection is held open indefinitely and the server never observes a close,
never fires request.signal's abort, and never runs the response stream's
cancel.

Fix

When the response body is still arriving at cancel time (result.has_more),
abort the fetch via the existing abort_task() (sets the aborted flag, cancels
the tracker, and schedule_shutdowns the connection) instead of draining, so
the connection closes and the server observes the cancellation. A fully
received body has nothing left to abort and still falls through to the
drain/cleanup path, leaving the socket reusable for keep-alive.
ignore_remaining_response_body now skips the drain when the fetch is aborted,
since the connection is already shutting down.

This routes both reader.cancel() and res.body.cancel(), over HTTP/1 and
HTTP/2, through the same shared path.

Verification

New regression test in test/regression/issue/33227.test.ts: a server streams
an SSE body and records request.signal abort + the response stream's cancel;
the client reads a chunk then calls reader.cancel() and awaits both
server-side callbacks firing.

  • With the fix: passes (~260ms).
  • Without the fix (git stash of src/): the callbacks never fire and the
    test times out.

test/js/web/fetch/fetch-stream-cancel-leak.test.ts (GC after
reader.cancel() / body.cancel()) and the abort-path tests
(fetch-abort-stream-body, fetch-abort-queued, fetch-abort-socket-close-race)
still pass.

Cancelling a fetch response body reader (reader.cancel() / body.cancel())
routed to ignore_remaining_response_body, which drained the rest of the
body to keep the connection alive. For an unbounded or streaming response
this held the socket open indefinitely, so the server never observed the
cancellation and never fired request.signal's abort or the response
stream's cancel.

When the body is still arriving, abort the fetch so the connection is shut
down and the server sees the close. A fully received body still falls
through to the drain/cleanup path so the socket can be reused.
@coderabbitai

coderabbitai Bot commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

Fetch cancellation now aborts in-flight requests on reader cancellation, skips response draining after abort, and makes abort handling idempotent. Regression tests cover server-side abort/cancel events and later request recovery.

Changes

Fetch cancellation abort behavior

Layer / File(s) Summary
Idempotent abort and cancel flow
src/runtime/webcore/fetch/FetchTasklet.rs
abort_task now returns early on repeated calls, on_stream_cancelled_callback calls abort_task() before draining, and ignore_remaining_response_body suppresses resume and response-body streaming when already aborted.
Server identity failures use abort_task
src/runtime/webcore/fetch/FetchTasklet.rs
check_server_identity failure paths now call abort_task() instead of issuing the abort and shutdown sequence inline.
Cancellation regression tests
test/regression/issue/33227.test.ts, test/js/web/fetch/fetch-backpressure.test.ts
A new regression test verifies client cancellation triggers server abort and stream cancel, and the backpressure test now checks a later keepalive request completes after mid-stream cancellation.

Possibly related PRs

  • oven-sh/bun#29831: Also adjusts FetchTasklet cancellation and remaining-body handling in the same fetch streaming flow.
  • oven-sh/bun#32130: Also modifies FetchTasklet.rs abort and cancellation handling around fetch streaming paths.
  • oven-sh/bun#32729: Also changes FetchTasklet::ignore_remaining_response_body and adjacent cancellation cleanup logic.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main behavior change: cancelling a response body reader now aborts the request.
Description check ✅ Passed The description covers the bug, cause, fix, and verification steps with a concrete repro, despite not using the exact template headings.
Linked Issues check ✅ Passed The changes implement the reported behavior and add a regression test showing reader.cancel triggers both request abort and server stream cancel.
Out of Scope Changes check ✅ Passed The code and test changes stay focused on the cancellation/abort regression and related behavior updates.

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the claude label Jul 2, 2026
@robobun

robobun commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 7:08 PM PT - Jul 3rd, 2026

@Jarred-Sumner, your commit 85ecd3f is building: #68277

#19211 was fixed then regressed, so this is a true regression; isolate it
from the timing-sensitive streaming tests in fetch.stream.test.ts.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (2)
src/runtime/webcore/fetch/FetchTasklet.rs (2)

1755-1804: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Skip receive-resume once the fetch is aborted drain_events() processes queued receive resumes before queued shutdowns, so this can still re-arm and drain an already-aborted socket. Guard it with signal_store.aborted before scheduling, or move the check ahead of the resume enqueue.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/webcore/fetch/FetchTasklet.rs` around lines 1755 - 1804, The
ignore_remaining_response_body flow can still enqueue a receive resume after the
fetch has already been aborted, because drain_events() may process that resume
before shutdown. Update ignore_remaining_response_body to check
signal_store.aborted before calling schedule_receive_resume(), or otherwise move
the aborted guard ahead of the resume enqueue so the FetchTasklet never re-arms
draining on an aborted socket.

1620-1632: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Avoid enqueuing duplicate shutdowns from abort_task()
on_stream_cancelled_callback() and abort_listener() can both reach abort_task(), but the shutdown path isn’t deduped: schedule_shutdown_by_id() always appends a ShutdownMessage, and a second call for the same fetch just turns into an extra no-op drain plus has_pending_queued_abort churn. Gate abort_task() on signal_store.aborted or make shutdown scheduling idempotent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/webcore/fetch/FetchTasklet.rs` around lines 1620 - 1632,
`on_stream_cancelled_callback()` can trigger `abort_task()` more than once for
the same fetch, which causes duplicate shutdown scheduling and unnecessary
queued-abort churn. Update the shutdown path in `abort_task()` and/or
`schedule_shutdown_by_id()` so it is idempotent by checking
`signal_store.aborted` before scheduling, or by deduping repeated shutdown
requests for the same task; keep the existing behavior in
`on_stream_cancelled_callback()` and `abort_listener()` but ensure only the
first abort enqueues a shutdown.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/regression/issue/33227.test.ts`:
- Around line 17-24: The test in pull(controller) is still allowing too much
data to buffer before reader.cancel() runs, so it may not exercise the in-flight
abort path. Reduce the number of eagerly enqueued chunks in the response stream
setup and keep the body pending sooner by relying on the existing holdOpen
behavior earlier, so the regression test targets the abort path instead of
buffered cleanup.

---

Outside diff comments:
In `@src/runtime/webcore/fetch/FetchTasklet.rs`:
- Around line 1755-1804: The ignore_remaining_response_body flow can still
enqueue a receive resume after the fetch has already been aborted, because
drain_events() may process that resume before shutdown. Update
ignore_remaining_response_body to check signal_store.aborted before calling
schedule_receive_resume(), or otherwise move the aborted guard ahead of the
resume enqueue so the FetchTasklet never re-arms draining on an aborted socket.
- Around line 1620-1632: `on_stream_cancelled_callback()` can trigger
`abort_task()` more than once for the same fetch, which causes duplicate
shutdown scheduling and unnecessary queued-abort churn. Update the shutdown path
in `abort_task()` and/or `schedule_shutdown_by_id()` so it is idempotent by
checking `signal_store.aborted` before scheduling, or by deduping repeated
shutdown requests for the same task; keep the existing behavior in
`on_stream_cancelled_callback()` and `abort_listener()` but ensure only the
first abort enqueues a shutdown.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 1fc860ef-a980-4637-b0a2-a8abb8497d25

📥 Commits

Reviewing files that changed from the base of the PR and between 5b55beb and 03692e2.

📒 Files selected for processing (2)
  • src/runtime/webcore/fetch/FetchTasklet.rs
  • test/regression/issue/33227.test.ts

Comment thread test/regression/issue/33227.test.ts Outdated
Comment thread src/runtime/webcore/fetch/FetchTasklet.rs Outdated
Address review findings on the reader-cancel abort path:

- Read result.has_more under self.mutex in on_stream_cancelled_callback.
  The HTTP thread replaces self.result wholesale under that mutex, so the
  unsynchronized read was a data race; a stale has_more could skip the abort.
- Make abort_task idempotent: reader.cancel() and an AbortSignal can both
  reach it, and the second call would queue a redundant shutdown.
- Skip schedule_receive_resume as well as enable_response_body_streaming once
  aborted; drain_events processes resumes before shutdowns, so a resume could
  re-arm draining on an already-closing socket.

Test: stream the response body forever (paced) so the unfixed drain path keeps
the connection open and the server never sees the close, making the regression
deterministic instead of racing a finite body's idle teardown.
@Jarred-Sumner

Copy link
Copy Markdown
Collaborator

@robobun verify the behavior in node and deno is the same

@robobun

robobun commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Verified against Node and Deno by running the issue's scenario as the client in each runtime (read one chunk, then reader.cancel()), and checking whether the server observes the cancellation. All three abort the request and the server sees the disconnect:

Runtime Result on reader.cancel()
Bun (this PR) request.signal aborts and the response stream's cancel() fires
Node v26.3.0 node:http server observes the client disconnect mid-stream (global fetch closes the connection)
Deno 2.9.1 request.signal aborts and the response stream's cancel() fires

Deno and Bun run the issue's server verbatim (request.signal + new Response(new ReadableStream({ cancel() {} }))). Node's http server doesn't expose a Web ReadableStream response with a cancel() hook, so there the signal is the connection closing before the response finished (res "close" with !writableFinished), which is how the server learns the client aborted.

Console output:

BUN:  aborted = true  cancelled = true
NODE: server observed client abort mid-stream = true
DENO: aborted = true  cancelled = true

One Deno nuance: it prints a deprecation note that request.signal aborting on an already-sent response is legacy behavior (denoland/deno#29111). The response body stream's cancel() fires regardless, and that is the direct analog of the server-side signal this PR restores.

Net: before this PR, Bun alone kept the connection open and drained the body, so neither callback fired; with the fix, Bun matches Node and Deno.

reader.cancel() on an in-flight body now aborts the request instead of
draining the abandoned body for keep-alive reuse, matching Node/Deno and
browsers (#33227). Update the drain-for-keepalive assertion accordingly:
the server no longer sends the whole first body, and a later request runs
on a fresh connection.
@robobun

robobun commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

CI surfaced a behavioral decision worth calling out. The one real failure was test/js/web/fetch/fetch-backpressure.test.tsreader.cancel() resumes the socket so the abandoned body drains (timed out on h1 across aarch64 / x64 / x64-baseline). The other red lanes were unrelated flakes (cpu-prof "No samples collected", bun-install-security-provider timeout, a bake production.test.ts that throws "oh no!" by design).

That test was added in #29831 and deliberately asserts the old drain-for-keepalive behavior: cancelling a 16 MiB in-flight body drains the whole thing so the connection can be pooled and reused (sent === 2 * TOTAL). That is exactly the behavior this PR changes, and exactly what Node and Deno do not do (per the verification above, both abort the connection on reader.cancel()).

So this is the tradeoff: with the fix, cancelling an in-flight response body aborts the request (closes the connection, server observes the cancel) and does not reuse that connection for keep-alive. A fully received body still drains/cleans up and stays poolable. I updated that test to assert the abort behavior (the first response no longer drains to completion; a follow-up request runs on a fresh connection), which matches Node/Deno/browsers.

Flagging in case you'd rather keep draining for small/near-complete bodies (some clients drain when the remainder is tiny and abort otherwise). Happy to add that heuristic if you want it, but the straight abort is what Node and Deno do here.

Comment thread test/js/web/fetch/fetch-backpressure.test.ts Outdated
… test

server.sent() < 2 * TOTAL is flaky on CI hosts with large TCP loopback
buffers (tcp_rmem[2]+tcp_wmem[2] up to ~256 MiB): the 16 MiB body fully
buffers during the stall, the first request completes, and sent reaches
2 * TOTAL. The sibling drain test uses a 1 GiB body for this exact reason.
Keep the recovery check (a later request completes); the abort-vs-drain
behavior is asserted directly in test/regression/issue/33227.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/runtime/webcore/fetch/FetchTasklet.rs (1)

2275-2287: 🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win

Route cert-check failures through abort_task()
The three check_server_identity failure branches still bypass the new idempotency guard by writing aborted and calling did_cancel/schedule_shutdown directly. Reuse abort_task() here so an earlier cancel path can’t enqueue the shutdown sequence twice.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/webcore/fetch/FetchTasklet.rs` around lines 2275 - 2287, The
three `check_server_identity` failure branches are still bypassing the
idempotent abort path by setting `aborted` and calling
`did_cancel`/`schedule_shutdown` directly. Update those failure paths in
`FetchTasklet::check_server_identity` to call `abort_task()` instead, so the
existing guard in `abort_task()` handles all cancellation and shutdown
sequencing exactly once.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@test/js/web/fetch/fetch-backpressure.test.ts`:
- Around line 303-314: The test in reader.cancel() mid-stream relies on an
arbitrary Bun.sleep(50) delay to keep the request in-flight, which makes it
flaky. Replace the timing-based wait with an actual condition in the
fetch-backpressure test, using the same reader-cancel flow and server streaming
behavior as the related regression test for issue 33227, so the cancel happens
only after confirming the stream is still active via observable I/O state rather
than wall-clock time.

---

Outside diff comments:
In `@src/runtime/webcore/fetch/FetchTasklet.rs`:
- Around line 2275-2287: The three `check_server_identity` failure branches are
still bypassing the idempotent abort path by setting `aborted` and calling
`did_cancel`/`schedule_shutdown` directly. Update those failure paths in
`FetchTasklet::check_server_identity` to call `abort_task()` instead, so the
existing guard in `abort_task()` handles all cancellation and shutdown
sequencing exactly once.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: a93a8931-5301-451d-8631-685c0db7932a

📥 Commits

Reviewing files that changed from the base of the PR and between 03692e2 and 9efbf56.

📒 Files selected for processing (3)
  • src/runtime/webcore/fetch/FetchTasklet.rs
  • test/js/web/fetch/fetch-backpressure.test.ts
  • test/regression/issue/33227.test.ts

Comment thread test/js/web/fetch/fetch-backpressure.test.ts
…n test

- check_server_identity's three failure branches set aborted / did_cancel /
  schedule_shutdown inline; route them through the now-idempotent abort_task()
  so every abort path shares one shutdown-once guard.
- Drop the Bun.sleep(50) in the backpressure cancel test; the prior
  reader.read() already confirms the stream is active, so the wait is not
  load-bearing for the recovery check.

@coderabbitai coderabbitai Bot 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.

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/runtime/webcore/fetch/FetchTasklet.rs (1)

1610-1614: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Keep the new comments within the 3-line limit.

These two added explanatory blocks exceed the repository comment-length rule; they can be condensed without losing the invariant.

Proposed wording
-        // reader.cancel() on a still-arriving body aborts the fetch so the
-        // connection closes and the server observes the cancellation. A fully
-        // received body is left to drain/cleanup so the socket stays reusable.
-        // `result` is replaced wholesale by the HTTP thread under `mutex`, so
-        // read `has_more` under the lock like the other JS-thread callbacks.
+        // reader.cancel() aborts a still-arriving body so the server sees disconnect;
+        // fully received bodies keep drain/cleanup for reuse. Read `has_more`
+        // under `mutex` because the HTTP thread replaces `result` wholesale.
-        // An aborted fetch (reader.cancel() on an in-flight body, or an
-        // AbortSignal) is already shutting the connection down; don't re-arm the
-        // receive or resume draining, which would read the rest of an unbounded
-        // body and hold the socket open (drain_events resumes before shutdowns).
+        // Aborted fetches are already shutting down; don't re-arm receive/resume
+        // draining, which can read an unbounded body and hold the socket open
+        // because drain_events resumes before shutdowns.

As per coding guidelines, "Keep code comments to 3 lines max." <coding_guidelines>

Also applies to: 1765-1768

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/runtime/webcore/fetch/FetchTasklet.rs` around lines 1610 - 1614, The
newly added explanatory comments in FetchTasklet should be shortened to satisfy
the 3-line comment limit without losing the key invariant. Condense the two
comment blocks near the `reader.cancel()` logic and the `has_more`
read-under-lock note into brief, focused comments, keeping the essential
behavior notes only. Update the comment text in `FetchTasklet` where these
explanatory blocks were added so each comment stays within the repository
guideline while still referencing the cancellation behavior, reusable socket
handling, and mutex-protected `result`/`has_more` access.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Outside diff comments:
In `@src/runtime/webcore/fetch/FetchTasklet.rs`:
- Around line 1610-1614: The newly added explanatory comments in FetchTasklet
should be shortened to satisfy the 3-line comment limit without losing the key
invariant. Condense the two comment blocks near the `reader.cancel()` logic and
the `has_more` read-under-lock note into brief, focused comments, keeping the
essential behavior notes only. Update the comment text in `FetchTasklet` where
these explanatory blocks were added so each comment stays within the repository
guideline while still referencing the cancellation behavior, reusable socket
handling, and mutex-protected `result`/`has_more` access.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6a5c83fa-c83a-4627-a964-62837470fd48

📥 Commits

Reviewing files that changed from the base of the PR and between 9efbf56 and e00e888.

📒 Files selected for processing (2)
  • src/runtime/webcore/fetch/FetchTasklet.rs
  • test/js/web/fetch/fetch-backpressure.test.ts
💤 Files with no reviewable changes (1)
  • test/js/web/fetch/fetch-backpressure.test.ts

@robobun

robobun commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Heads up on CI: the only red lane is :darwin: 26 aarch64 - test-bun, and it's failing on a Buildkite infra step before it runs any tests:

Error: buildkite-agent artifact download timed out after 120s for step 'darwin-aarch64-build-bun'. Refusing to continue with a partial download (would silently fall back to the wrong binary).

This is the artifact-download step fetching the already-built darwin-aarch64 binary from S3, not the test run, and it's recurred at the identical step across builds (67836 and 67845). This diff is a few lines of fetch cancel logic plus tests, so it can't affect artifact size or the build/upload step.

The change itself is verified: locally the new test/regression/issue/33227.test.ts passes (and times out on a from-main build), fetch-backpressure.test.ts is 42/42, and the 9 checkServerIdentity tests pass after routing cert-check aborts through abort_task. The Windows x64-baseline test lane also passed on the prior build.

A re-run of the darwin-aarch64 lane (or a merge, if you're satisfied with the review) should clear it. Happy to push a retrigger if you'd prefer I re-roll it.

Comment thread src/runtime/webcore/fetch/FetchTasklet.rs Outdated
Comment thread src/runtime/webcore/fetch/FetchTasklet.rs
…e lock

on_stream_cancelled_callback now always calls the idempotent abort_task()
instead of reading result.has_more under the mutex. Node/Deno/browsers abort
unconditionally, and the cancel handler is cleared on full body delivery
(clear_stream_handlers), so the callback effectively only fires mid-stream
anyway. This removes both the off-thread result data race and the reentrant
mutex lock (reachable when a request-body sink cancel handler cancels the
response reader while on_progress_update holds the mutex).
Comment thread src/runtime/webcore/fetch/FetchTasklet.rs Outdated
abort_task() also calls tracker.did_cancel (touches JSC), so 'only touches
atomics + schedule_shutdown' was wrong and could mislead someone into thinking
it is safe from the GC finalizer. Keep only the accurate 'idempotent' note.
@robobun

robobun commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator Author

Update on CI now that more lanes have reported on build 67862: the red is entirely from failures unrelated to this diff.

  • test/bake/dev/production.test.ts ("works with sourcemaps - error thrown in React component"): fails deterministically on all ~14 platforms. It's a bake/SSR test with no fetch involvement, so it looks like a main-branch / bake-harness break, not this change.
  • :darwin: 26 aarch64 - test-bun: the recurring Buildkite artifact-download infra timeout for darwin-aarch64-build-bun (before it runs any tests).
  • test/js/web/fetch/body-stream.test.ts (4/15 platforms) and test/js/third_party/grpc-js/test-tonic.test.ts (2 platforms): flaky timeouts on a subset of platforms. body-stream.test.ts contains zero .cancel() / AbortController / .signal usage, so this PR's cancel/abort change cannot affect it.

This diff is a few lines of fetch reader-cancel logic plus its tests; none of the red lanes exercise it (211 test shards passed). A ci: retrigger won't go green while production.test.ts fails deterministically on every platform, so I'm not spinning it. Flagging for a maintainer: once the bake lane and the darwin artifact infra are sorted (or if you're happy with the review), this should be good to merge.

@Jarred-Sumner
Jarred-Sumner merged commit 85300d2 into main Jul 4, 2026
77 of 78 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the farm/469333cb/fetch-reader-cancel-abort branch July 4, 2026 02:58
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Bug: Calling ReadableStream.cancel in client should abort the request and trigger the ReadableStream.cancel on the server

2 participants