Skip to content

Conversation

@matthew2564
Copy link

Summary

Fixes #6827

When writing empty content (e.g., new Response("")), the truncate syscall fails with ENOENT if the file doesn't exist. Previously, this would error out when createPath: false even if the parent directory existed.

Now we attempt to create the file with open(O_WRONLY | O_CREAT | O_TRUNC) before checking mkdirp_if_not_exists, correctly separating file creation from parent directory creation.

  • createPath: false now only prevents creating missing parent directories, not the file itself
  • Added O_WRONLY flag to open calls for consistency with the rest of the codebase

Test plan

  • Added regression test at test/regression/issue/06827.test.ts covering:
    • Empty Response creates file
    • Empty Blob creates file
    • Empty Response truncates existing file
    • Empty string baseline (already worked)
    • createPath: false still creates file when parent exists
    • createPath: false errors when parent dir missing
  • Ran full bun-write.test.js suite (30/30 pass)

@matthew2564 matthew2564 force-pushed the bun-write-empty-response-6827 branch from 87dd289 to 1d90308 Compare January 25, 2026 11:35
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jan 25, 2026

Walkthrough

Modify file-write logic to ensure empty Responses/Blobs create or truncate destination files; on ENOENT while opening for truncate/create, conditionally create missing parent directories (mkdirp) and retry. Add regression tests covering empty writes and createPath semantics.

Changes

Cohort / File(s) Summary
File write implementation
src/bun.js/webcore/Blob.zig
On open with `WRONLY
Regression tests
test/regression/issue/06827.test.ts
New tests verifying Bun.write behavior for empty Response and Blob, truncation of existing files, baseline empty-string behavior, and createPath: false semantics (file creation vs ENOENT for missing parent dir).

Suggested reviewers

  • alii
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title accurately describes the main fix: enabling file creation for empty Response when createPath is false, which directly addresses issue #6827.
Description check ✅ Passed The description includes all required sections (What does this PR do and How did you verify), with comprehensive details about the fix, test coverage, and verification steps.
Linked Issues check ✅ Passed The PR directly addresses issue #6827 by implementing file creation for empty Response bodies and ensuring createPath: false only prevents parent directory creation, matching the issue's requirements.
Out of Scope Changes check ✅ Passed All changes are directly related to fixing the empty Response writing issue, including the core logic fix in Blob.zig and comprehensive regression tests.

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


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

…se (oven-sh#6827)

When writing empty content (e.g., `new Response("")`), the truncate syscall
fails with ENOENT if the file doesn't exist. Previously, this would error
out when `createPath: false` even if the parent directory existed.

Now we attempt to create the file with open(O_WRONLY | O_CREAT | O_TRUNC)
before checking mkdirp_if_not_exists, correctly separating file creation
from parent directory creation.
@matthew2564 matthew2564 force-pushed the bun-write-empty-response-6827 branch from 1d90308 to cc6e24e Compare January 26, 2026 18:34
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In `@src/bun.js/webcore/Blob.zig`:
- Around line 934-938: The .fd arm in the second open attempt inside the while
loop in Blob.zig is dead code because any .fd pathlike would have triggered the
earlier err-handling branch and not reached the mkdirp/reopen logic; update the
switch on file.pathlike in that re-open block (the open_res assignment inside
the while loop) to either remove the .fd case and only handle .path, or replace
the .fd arm with `@unreachable`() to make the intent explicit and satisfy the
compiler, ensuring you keep the same flags (bun.O.WRONLY | bun.O.CREAT |
bun.O.TRUNC) and refer to the same buf sliceZ call used for the .path case.

Comment on lines 934 to +938
while (true) {
const open_res = bun.sys.open(file.pathlike.path.sliceZ(&buf), bun.O.CREAT | bun.O.TRUNC, mode);
const open_res = switch (file.pathlike) {
.path => |path| bun.sys.open(path.sliceZ(&buf), bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, mode),
.fd => break :err,
};
Copy link
Contributor

Choose a reason for hiding this comment

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

🧹 Nitpick | 🔵 Trivial

The .fd case in the second open attempt is unreachable.

After mkdirp succeeds, we re-open the file. However, if file.pathlike were .fd, we would have already exited the err block at line 912-919 before reaching mkdirp. This .fd case at line 937 is dead code.

♻️ Consider simplifying by using `unreachable` or removing the switch
                        while (true) {
-                            const open_res = switch (file.pathlike) {
-                                .path => |path| bun.sys.open(path.sliceZ(&buf), bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, mode),
-                                .fd => break :err,
-                            };
+                            // pathlike must be .path here - .fd case exits earlier at line 912-919
+                            const open_res = bun.sys.open(file.pathlike.path.sliceZ(&buf), bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC, mode);
🤖 Prompt for AI Agents
In `@src/bun.js/webcore/Blob.zig` around lines 934 - 938, The .fd arm in the
second open attempt inside the while loop in Blob.zig is dead code because any
.fd pathlike would have triggered the earlier err-handling branch and not
reached the mkdirp/reopen logic; update the switch on file.pathlike in that
re-open block (the open_res assignment inside the while loop) to either remove
the .fd case and only handle .path, or replace the .fd arm with `@unreachable`()
to make the intent explicit and satisfy the compiler, ensuring you keep the same
flags (bun.O.WRONLY | bun.O.CREAT | bun.O.TRUNC) and refer to the same buf
sliceZ call used for the .path case.

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.

Bun write from empty Response doesn't create any file.

1 participant