Skip to content

Remove stale signal.is_dead() assertion in pipe_readable_stream_to_blob - #32657

Merged
Jarred-Sumner merged 4 commits into
mainfrom
farm/25f9fc0e/blob-pipe-signal-dead
Jun 25, 2026
Merged

Jarred-Sumner merged 4 commits into
mainfrom
farm/25f9fc0e/blob-pipe-signal-dead

Conversation

@robobun

@robobun robobun commented Jun 24, 2026

Copy link
Copy Markdown
Collaborator

Fuzzilli found a debug assertion failure: panic: assertion failed: !signal.get().is_dead() at src/runtime/webcore/Blob.rs:1653.

Minimal repro:

Bun.file(tmp).write(Bun.S3Client.file("key"));

What was happening

pipe_readable_stream_to_blob sets the sink signal to a dead sentinel, calls FileSink__assignToStream, then asserts the signal is no longer dead (i.e. that C++ wrote the controller cell into it).

C++ does always write the controller into signal.ptr before running the JS assignToStream builtin. But since #32120 added __controllerDetached, calling controller.end() / .close() during that JS clears the signal back to dead. When the source stream's pull() fails synchronously (an S3 file with no credentials throws ERR_S3_MISSING_CREDENTIALS), the sink is ended before assignToStream returns, the signal is cleared, and the assertion trips.

The code after the assertion already handles this correctly (error/promise-status branches), and RequestContext.rs already dropped its equivalent post-call assertion for the same reason. This change drops the stale assertion here and documents why.

How did you verify your code works?

New test in test/js/bun/s3/s3-write-to-file-sync-close.test.ts spawns a child that writes an S3 file (no credentials) to a file Blob. Fails on the current debug build with the assertion panic; passes with this change.

The controllerDetached hook added in #32120 clears the sink signal when
controller.end()/.close() is called. When a source stream finishes
synchronously inside assignToStream (for example an S3 file with no
credentials, which throws in pull()), the controller detaches before
assignToStream returns and the signal is legitimately dead. The
assertion predates that hook; RequestContext.rs already dropped its
equivalent assertion with the same reasoning.
@robobun

robobun commented Jun 24, 2026 •

Copy link
Copy Markdown
Collaborator Author
Updated 7:09 PM PT - Jun 24th, 2026

✅ @Jarred-Sumner, your commit 56b0ec0e81acbd8683f2799b18c41d82cd7d0e37 passed in Build #64563! 🎉


🧪   To try this PR locally:

bunx bun-pr 32657

That installs a local version of the PR into your bun-32657 executable, so you can run:

bun-32657 --bun

@coderabbitai

coderabbitai Bot commented Jun 24, 2026 •

Copy link
Copy Markdown
Contributor

Review Change Stack

Caution

Review failed

The pull request is closed.

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 77f2f3bf-ad96-4d3e-9ee9-251c05e10215

📥 Commits

Reviewing files that changed from the base of the PR and between fb1212a and 56b0ec0.

📒 Files selected for processing (1)
  • src/runtime/webcore/Blob.rs

Disabled knowledge base sources:

  • Linear integration is disabled

You can enable these sources in your CodeRabbit configuration.


Walkthrough

Removes a debug assertion in BlobExt::pipe_readable_stream_to_blob and adds a regression test for Bun.file(...).write() using an S3 file source with missing credentials.

Changes

FileSink synchronous close handling

Layer / File(s) Summary
Assertion removal and regression test
src/runtime/webcore/Blob.rs, test/js/bun/s3/s3-write-to-file-sync-close.test.ts
pipe_readable_stream_to_blob no longer asserts that the FileSink signal is live immediately after assign_to_stream. A new bun:test case spawns a subprocess that writes from Bun.S3Client.file(...) with S3 and AWS credentials cleared, then asserts on stdout, stderr, and exit status.

Possibly related PRs

  • oven-sh/bun#30953: Related FileSink lifecycle changes around initialization and signal state when starting with an invalid input path.
  • oven-sh/bun#31135: Adjusts FileSink end/error handling and signal completion behavior in the same area.
  • oven-sh/bun#32120: Changes controller-detach plumbing that can clear the stored signal earlier.

Suggested reviewers

  • Jarred-Sumner
  • cirospaciari
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately and specifically summarizes the main change: removing a stale signal assertion in the pipe_readable_stream_to_blob function.
Description check ✅ Passed The description provides comprehensive information covering both required template sections: what the PR does (removes the assertion and documents why) and how the fix was verified (new test case included).
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


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

@github-actions

Copy link
Copy Markdown
Contributor

Found 1 issue this PR may fix:

  1. pipeReadableStreamToBlob assertion failure on Windows when readStreamIntoSink completes synchronously #28090 - Describes the same pipeReadableStreamToBlob assertion failure when a stream completes synchronously, leaving the signal dead

If this is helpful, copy the block below into the PR description to auto-close this issue on merge.

Fixes #28090

🤖 Generated with Claude Code

Comment thread test/js/bun/s3/s3-write-to-file-sync-close.test.ts

@claude claude 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.

LGTM — the test now accepts either resolve or reject per the earlier feedback, and the Rust change is a debug-assert removal with no release-build impact.

Extended reasoning...

Overview

This PR removes a single debug_assert!(!signal.get().is_dead()) in pipe_readable_stream_to_blob (src/runtime/webcore/Blob.rs) and replaces it with a comment explaining why the signal may legitimately be dead after assignToStream returns — since #32120, synchronous stream completion runs __controllerDetached which clears the signal back to dead before control returns. It also adds a regression test (test/js/bun/s3/s3-write-to-file-sync-close.test.ts) that reproduces the fuzzilli-found panic via Bun.file().write(Bun.S3Client.file(...)) with no credentials.

Security risks

None. The only runtime change is removing a debug-build-only assertion; release builds are byte-identical in behavior. The test spawns a subprocess with S3/AWS credential env vars explicitly unset, so it cannot accidentally hit a real bucket.

Level of scrutiny

Low. The Rust change is purely subtractive in debug builds and a no-op in release. The code paths immediately following the removed assertion (error → rejected promise; promise Pending/Fulfilled/Rejected branches) already handle all states without referencing the signal, and RequestContext.rs already dropped its equivalent post-assignToStream assertion for the same reason — only the pre-call is_dead() check remains there, mirroring what Blob.rs now has.

Other factors

I left an inline comment on the earlier revision noting the test was pinning a pre-existing swallowed-error bug as the contract. Commit fb1212a addressed that exactly as suggested (Option B): the fixture now wraps the write() in try/catch, the assertion accepts either resolved or rejected:ERR_S3_MISSING_CREDENTIALS, and the file header documents the swallowed-error situation so a future fix to ByteStream::on_start() won't trip this test. No CODEOWNERS cover these paths. The robobun musl build failures reference the older commit (617613c) and are infra (scripts/build/ci.ts) failures unrelated to a comment-only Rust change and a new test file.

@Jarred-Sumner
Jarred-Sumner merged commit ed700c2 into main Jun 25, 2026
5 of 7 checks passed
@Jarred-Sumner
Jarred-Sumner deleted the farm/25f9fc0e/blob-pipe-signal-dead branch June 25, 2026 01:33

@claude claude 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.

LGTM — thanks for updating the test to accept either resolve or reject.

Extended reasoning...

Overview

This PR removes a single debug_assert!(!signal.get().is_dead()) in pipe_readable_stream_to_blob (src/runtime/webcore/Blob.rs) and adds a regression test (test/js/bun/s3/s3-write-to-file-sync-close.test.ts). The assertion was checking that C++ wrote the controller cell into the sink signal during assignToStream, but since #32120 introduced __controllerDetached, a synchronously-completing source stream (e.g., S3 with no credentials) can legitimately end the sink and clear the signal back to dead before assignToStream returns. The code immediately following the removed line already handles all outcomes (error, fulfilled, rejected, pending promise), and RequestContext.rs already dropped the equivalent assertion for the same reason.

Security risks

None. This is a debug_assert! — it compiles out of release builds entirely, so no production behavior changes. The test only spawns a child Bun process with credential env vars unset; no network/auth/crypto surface is touched.

Level of scrutiny

Low. A 3-line deletion of a debug-only assertion, with clear precedent in the codebase and a well-reasoned PR description tracing the exact code path that invalidates the invariant. The new test is isolated and self-contained.

Other factors

I previously flagged that the original test assertions locked in a pre-existing swallowed-error bug as the contract. The author addressed this in commit fb1212a: the fixture now wraps the write() in try/catch, the test accepts either resolved or rejected:ERR_S3_MISSING_CREDENTIALS, and a header comment documents that ByteStream::on_start() currently swallows the error so a future fix won't trip this test. That inline comment is now resolved. No bugs were found in the current revision and there are no outstanding reviewer comments.

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.

2 participants