Repository navigation
Conversation
Node.js passes fs.readv/readvSync/FileHandle#readv through to uv_fs_read, which returns UV_EINVAL when nbufs == 0. Node only short-circuits the empty-array case for writev (in JS, before libuv), so the read and write sides are asymmetric. Bun was returning bytesRead=0 for readv([]) in all three forms, silently succeeding where Node throws. Change the readv empty-buffers short-circuit to return an EINVAL error with syscall "read" (matching Node's error shape exactly), and mirror the guard in the Windows libuv async path where uv_fs_read would have returned UV_EINVAL synchronously instead of invoking the callback.
|
Updated 1:10 PM PT - Jul 16th, 2026
⏳ @robobun, your commit 254f2fc is still building in |
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Run ID: 📒 Files selected for processing (2)
WalkthroughChangesEmpty-buffer readv behavior
Suggested reviewers: 🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
Comment |
There was a problem hiding this comment.
LGTM — small Node.js compat fix matching libuv's uv_fs_read(nbufs==0) → UV_EINVAL behavior.
Checked that the Windows async guard mirrors the existing Writev empty-buffers pattern at node_fs.rs:889-903 (same core::ptr::write + enqueue_task sequence), and that the sync path's sys::Error construction matches neighboring error sites. Verified writev([]) remains a 0-return on both the sync and Windows-async paths. Tests cover sync/callback/FileHandle plus the positional variant, use tempDir with proper cleanup, and pin the unchanged writev contract.
Extended reasoning...
Overview
Two-file change: src/runtime/node/node_fs.rs swaps the empty-buffers short-circuit in readv from Ok(bytes_read: 0) to Err(EINVAL, syscall=read), and adds a matching guard to the Windows libuv async Readv arm so uv_fs_read is never called with nbufs==0 (which would return synchronously and never fire the callback). test/js/node/fs/fs.test.ts gains a describe block covering sync, callback, and FileHandle forms for both readv (now EINVAL) and writev (still 0).
Security risks
None. No new input parsing, no path handling, no allocation sizing — this replaces a success return with an error return on an already-validated empty array.
Level of scrutiny
Low. This is a targeted Node.js compatibility fix with a well-documented upstream rationale (libuv's explicit nbufs==0 → UV_EINVAL guard, which Node surfaces for readv but short-circuits in JS for writev). The Windows async change is a byte-for-byte copy of the existing Writev empty-buffers handling immediately below it (lines 889-903), just writing Err instead of Ok. The sync change is a one-expression swap using the same sys::Error { errno, syscall, fd, ..Default::default() } shape as the neighboring preadv_inner error path.
Other factors
Tests follow harness conventions (tempDir, using/await using, try/finally around fd), assert exact error shape (code + syscall), and cover the positional overload and the negative contract (writev([]) unchanged). The bug hunting system found no issues. rust:check-all reportedly passes for all targets.
|
Closing as part of a cleanup of stale pull requests. This PR has had no new commits since 2026-07-16, it conflicts with main, and its last CI run failed. This is not a judgment on the fix itself. If the problem still reproduces on a current build, reopen this PR after a rebase or open a new one against main. |
What does this PR do?
fs.readv(fd, []),fs.readvSync(fd, []), andFileHandle#readv([])now throwEINVALinstead of returningbytesRead: 0, matching Node.js.Reproduction
Node v26.3.0:
EINVAL readBun before:
0Bun after:
EINVAL readCause
Node routes
readvthroughuv_fs_read, which has an explicitnbufs == 0 -> UV_EINVALguard. Node only short-circuits the empty-array case forwritev(in JS, before reaching libuv), sowritev(fd, [])returns 0 whilereadv(fd, [])throws. Bun'sreadv()insrc/runtime/node/node_fs.rswas short-circuiting both to 0.Fix
Change the
readvempty-buffers short-circuit to returnEINVALwith syscall"read", matching Node's error object exactly ({code: "EINVAL", errno: -22, syscall: "read"}). The Windows libuv async path gets the same guard; without it,uv_fs_readwould returnUV_EINVALsynchronously rather than via the callback, leaving the request unresolved.writev(fd, [])is unchanged and still returns 0.How did you verify your code works?
New tests in
test/js/node/fs/fs.test.tscover all three forms (sync, callback,FileHandle) plus the positional variant, and assert both thereadv -> EINVALandwritev -> 0sides. All fail on currentmainand pass with the fix.bun run rust:check-allpasses for all targets.no test proof · iteration 0 · Platform-specific test(s) that do not run on this machine. Deferring to CI, which covers all platforms: test/js/node/fs/fs.test.ts