Skip to content

Bun.write(Bun.stdout, data) pads an appended file with NUL bytes when data is over 1024 bytes (fallocate on an O_APPEND fd) #44387

Open

Description

@Vantrongs

What version of Bun is running?

1.4.2+744846f84

What platform is your computer?

Linux 7.2.7 x86_64 unknown

What steps can reproduce the bug?

printf 'abc\n' > out.log
bun -e 'await Bun.write(Bun.stdout, "x".repeat(1999) + "\n")' >> out.log
wc -c < out.log                 # 4000
tr -cd '\0' < out.log | wc -c   # 1996

It needs stdout opened with O_APPEND (>>), more than 1024 bytes written, and a file shorter than the data. With 1024 bytes, or with a file already longer than the data, the output is correct.

What is the expected behavior?

out.log is abc\n followed by the 2000 bytes, 2004 bytes in total, as with process.stdout.write or fs.writeFileSync(1, …).

What do you see instead?

4000 bytes: abc\n, then 1996 NUL bytes, then the data.

strace shows

fallocate(1, 0, 0, 2000) = 0
write(1, "xxxx"..., 2000) = 2000

fallocate with mode 0 grows the file to 2000 bytes, and the O_APPEND write then lands after the new end.

Additional information

The preallocation is in WriteFile: for more than 1024 bytes on Linux it calls preallocate_file(fd, 0, len), which is fallocate(fd, 0, 0, len), without checking how the fd was opened. The equivalent preallocation in node:fs already skips preallocation for O_APPEND fds and uses the current offset from lseek; doing the same here, or passing FALLOC_FL_KEEP_SIZE, would fix it.

We hit it in a merge script: the JSON output of a Bun script, appended to a log with >>, was preceded by 211,016 NUL bytes.

Activity

  1. Vantrongs commented on Oct 1, 2026

    @Vantrongs
    Author

    Open PR #41685 fixes this: it skips the preallocation when the fd has O_APPEND, the same check node:fs makes. It also fixes a second failure on the same kind of fd, which we hit on 1.4.2 as well: Bun.write(Bun.stdout, Bun.file(p)) under >> rejects with EBADF: bad file descriptor, copy_file_range.

  2. robobun commented on Oct 3, 2026

    @robobun
    Collaborator

    An update on the two PRs named in this issue.

    #41685 no longer skips the preallocation. It now has only the EBADF: bad file descriptor, copy_file_range change for Bun.write(fd, Bun.file(p)) on an O_APPEND fd, and it stops clearing O_APPEND on a shared tty. The NUL padding that this issue reports is handled in #44425, which makes the preallocation keep the file size for every caller.

    I ran the repro above on a debug build of main with both PRs applied: out.log is 2004 bytes with no NUL byte, and Bun.write(Bun.stdout, Bun.file(p)) under >> appends and exits 0. On a build of main without them it is 4000 bytes with 1996 NUL bytes, and the file source rejects with EBADF.

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

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workinglinuxAn issue that occurs on Linuxneeds triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions