Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/io/PipeWriter.rs
Original file line number Diff line number Diff line change
Expand Up @@ -99,6 +99,9 @@ pub trait PosixPipeWriter {
write_fn: fn(Fd, &[u8]) -> sys::Result<usize>,
) -> WriteResult {
let fd = self.get_fd();
if fd == Fd::INVALID {
return WriteResult::Done(0);
}

let mut offset: usize = 0;

Expand Down
6 changes: 4 additions & 2 deletions src/runtime/webcore/FileSink.rs
Original file line number Diff line number Diff line change
Expand Up @@ -1240,7 +1240,8 @@ impl FileSink {
sys::Result::Ok(())
}
WriteResult::Err(e) => {
self.writer.with_mut(|w| w.close());
self.done.set(true);
self.writer.with_mut(|w| w.end());
sys::Result::Err(e)
}
WriteResult::Pending(written) => {
Expand Down Expand Up @@ -1354,7 +1355,8 @@ impl FileSink {
sys::Result::Ok(JSValue::js_number(written as f64))
}
WriteResult::Err(err) => {
self.writer.with_mut(|w| w.close());
self.done.set(true);
self.writer.with_mut(|w| w.end());
sys::Result::Err(err)
}
WriteResult::Pending(pending_written) => {
Expand Down
30 changes: 30 additions & 0 deletions test/js/bun/util/filesink.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -278,3 +278,33 @@ it("start() without path/fd on an already-open writer does not crash", async ()
await writer.end();
expect(await Bun.file(path).text()).toBe("hello");
});

it.skipIf(!isPosix)("writing after end() fails during flush does not crash", async () => {
const dir = tmpdirSync();

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.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Use tempDir fixture instead of tmpdirSync in this new test.

This new test introduces tmpdirSync(), which is against the repo test harness rule and weakens cleanup guarantees.

Suggested change
-  const dir = tmpdirSync();
-  const target = join(dir, "ro.txt");
+  using dir = tempDir("filesink-end-fail-flush", {});
+  const target = join(dir, "ro.txt");

As per coding guidelines: "**/*.test.{ts,tsx}: Use tempDir from 'harness' to create temporary directories - do not use tmpdirSync or fs.mkdtempSync."

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const dir = tmpdirSync();
using dir = tempDir("filesink-end-fail-flush", {});
const target = join(dir, "ro.txt");
🤖 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 `@test/js/bun/util/filesink.test.ts` at line 283, Replace the use of
tmpdirSync() assigned to the variable dir with the test harness tempDir fixture:
import { tempDir } from 'harness' (if not already imported) and replace const
dir = tmpdirSync(); with const dir = await tempDir(); (or const dir = tempDir();
depending on the fixture API) so the test uses the harness-managed temporary
directory and automatic cleanup instead of fs/tmpdirSync. Ensure any code that
expects a string path still works with the fixture return value.

const target = join(dir, "ro.txt");
fs.writeFileSync(target, "");
const writer = Bun.file(target).writer();
// Re-point the writer at a read-only fd so the buffered flush in end() fails.
const fd = fs.openSync(target, "r");
try {
writer.start({ fd });
} finally {
fs.closeSync(fd);
}
writer.write("x");
let endErr: unknown;
try {
await writer.end();
} catch (e) {
endErr = e;
}
expect(endErr).toBeDefined();
// Previously this would attempt to write to an invalid fd and crash with a
// debug assertion; now it should behave as if the sink is closed.
expect(() => writer.write("y")).not.toThrow();
expect(() => writer.start({})).not.toThrow();
expect(() => writer.write("z")).not.toThrow();
expect(() => writer.flush()).not.toThrow();
await Promise.resolve(writer.end()).catch(() => {});
Comment on lines +304 to +308

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.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

flush() assertion is sync-only and can miss async failures.

expect(() => writer.flush()).not.toThrow() only validates synchronous throw. If flush() rejects asynchronously, this test can produce false positives or unhandled rejection noise.

Suggested change
-  expect(() => writer.flush()).not.toThrow();
-  await Promise.resolve(writer.end()).catch(() => {});
+  await expect(writer.flush()).resolves.toBeUndefined();
+  await Promise.resolve(writer.end()).catch(() => {});
🤖 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 `@test/js/bun/util/filesink.test.ts` around lines 304 - 308, The test currently
uses a synchronous assertion for writer.flush() which only catches sync throws;
change it to await the promise and assert it resolves (e.g., use await
expect(writer.flush()).resolves.toBeUndefined() or await writer.flush() inside
try/catch) so async rejections fail the test, and similarly replace the
Promise.resolve(writer.end()).catch(() => {}) pattern with an awaited assertion
or try/catch (await writer.end() or await
expect(writer.end()).resolves.toBeUndefined()) to surface async errors from
writer.end().

await 1;
});
Loading