sys: map getcwd ENOENT to CurrentWorkingDirectoryUnlinked#32357
sys: map getcwd ENOENT to CurrentWorkingDirectoryUnlinked#32357robobun wants to merge 3 commits into
Conversation
Zig's std.posix.getcwd maps ENOENT to error.CurrentWorkingDirectoryUnlinked, which crash_handler::handle_root_error recognizes and turns into the actionable hint "The current working directory was deleted...". The Rust bun_sys::Error -> bun_core::Error conversion dropped the syscall tag and emitted a bare ENOENT, so the hint was dead code and users saw the generic "Bun could not find a file" fallback. Route From<bun_sys::Error> through to_zig_err() and special-case (Tag::getcwd, ENOENT) there; apply the same mapping in bun_core::getcwd which bypasses bun_sys::Error entirely.
WalkthroughWhen ChangesDeleted-CWD error mapping and test coverage
Possibly related PRs
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
Found 1 issue this PR may fix:
🤖 Generated with Claude Code |
There was a problem hiding this comment.
Caution
Some comments are outside the diff and can’t be posted inline due to platform limitations.
⚠️ Outside diff range comments (1)
test/cli/run/run-crash-handler.test.ts (1)
94-121: 🧹 Nitpick | 🔵 Trivial | ⚡ Quick winConsider making these tests concurrent.
The tests spawn subprocesses and use temp directories, so they should use
test.concurrentper the guideline to prefer concurrent tests when spawning processes or doing file I/O.♻️ Refactor options
Option 1: Add
.concurrentto existing testsfor (const cmd of [ ["-e", "console.log(1)"], ["run", "foo"], ]) { - test(`bun ${cmd[0]} prints the cwd-deleted hint`, async () => { + test.concurrent(`bun ${cmd[0]} prints the cwd-deleted hint`, async () => {Option 2: Use
test.concurrent.each(more idiomatic)- for (const cmd of [ - ["-e", "console.log(1)"], - ["run", "foo"], - ]) { - test(`bun ${cmd[0]} prints the cwd-deleted hint`, async () => { + test.concurrent.each([ + [["-e", "console.log(1)"]], + [["run", "foo"]], + ])(`bun %s prints the cwd-deleted hint`, async (cmd) => { using dir = tempDir("cwd-unlinked", {}); const gone = String(dir); await using proc = Bun.spawn({ cmd: [ "/bin/sh", "-c", `cd "${gone}" && rmdir "${gone}" && exec "${bunExe()}" ${cmd.map(a => `'${a}'`).join(" ")}`, ], env: bunEnv, stdout: "pipe", stderr: "pipe", }); const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); expect({ stdout, stderr, exitCode }).toEqual({ stdout: "", stderr: expect.stringContaining("The current working directory was deleted"), exitCode: 1, }); expect(stderr).not.toContain("Bun could not find a file"); }); - }🤖 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/cli/run/run-crash-handler.test.ts` around lines 94 - 121, The test cases in this block spawn subprocesses and use temp directories, which should run concurrently per the testing guidelines. Refactor the for loop iterating over the cmd array by replacing the standard test function calls with test.concurrent.each to make these tests concurrent, or alternatively add the .concurrent modifier to the existing test function call. The idiomatic approach is to use test.concurrent.each which will handle both the iteration and concurrent execution of the test cases.
🤖 Prompt for all review comments with 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.
Outside diff comments:
In `@test/cli/run/run-crash-handler.test.ts`:
- Around line 94-121: The test cases in this block spawn subprocesses and use
temp directories, which should run concurrently per the testing guidelines.
Refactor the for loop iterating over the cmd array by replacing the standard
test function calls with test.concurrent.each to make these tests concurrent, or
alternatively add the .concurrent modifier to the existing test function call.
The idiomatic approach is to use test.concurrent.each which will handle both the
iteration and concurrent execution of the test cases.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro
Run ID: 4a2c4406-2c27-4871-a8f2-4ef06abac029
📒 Files selected for processing (1)
test/cli/run/run-crash-handler.test.ts
|
Updated 4:29 PM PT - Jun 15th, 2026
✅ @robobun, your commit 990dd053cdddfd023e59bf4ccdde7e0d28d5e6c8 passed in 🧪 To try this PR locally: bunx bun-pr 32357That installs a local version of the PR into your bun-32357 --bun |
There was a problem hiding this comment.
LGTM — small, well-scoped error-name mapping that restores the Zig-era cwd-deleted hint, with tests.
Extended reasoning...
Overview
This PR restores the actionable "current working directory was deleted" message when bun starts in an unlinked cwd. It touches three Rust files and two test files:
src/sys/Error.rs: adds a single branch into_zig_err()soTag::getcwd+ENOENTreturns the internedCurrentWorkingDirectoryUnlinkedname.src/sys/lib.rs:From<bun_sys::Error> for bun_core::Errornow callse.to_zig_err()instead ofbun_core::Error::from_errno(e.errno as i32). Sinceto_zig_err()'s fallback iserrno_to_err(self.errno)→bun_core::Error::from_errno(errno as i32), this is behaviorally identical for every existing error and only adds the one new mapping.src/bun_core/util.rs:getcwd()(which callslibc::getcwddirectly and bypassesbun_sys::Error) now captureslast_os_error()once, checks forENOENT, and returns the named error; all other errnos go through the existing.into()path unchanged.- Tests: a new POSIX-only
describeinrun-crash-handler.test.tsexercisesbun -eandbun runfrom a deleted cwd, and the existing compiled-binary deleted-cwd test inbun-build-compile.test.tsupdates its stderr assertion fromENOENTto the human-readable hint.
I confirmed the consumer side matches: crash_handler::handle_root_error already has an arm comparing against bun_core::err!("CurrentWorkingDirectoryUnlinked") that prints exactly the string the tests assert on.
Security risks
None. This only refines which interned error name is produced for one (syscall, errno) pair on an error path that already terminates the process. No new I/O, no parsing, no auth/permissions/crypto, no user-controlled data flowing anywhere new.
Level of scrutiny
Low. This is an error-message/UX fix that re-establishes parity with the Zig stdlib's std.posix.getcwd ENOENT→error.CurrentWorkingDirectoryUnlinked mapping. The only change with broad reach — the From impl — is a pure refactor to call an existing helper whose fallback is byte-for-byte equivalent to the old body, so there's no collateral behavior change for other errnos or syscalls.
Other factors
The bug hunter found no issues. There are no outstanding human review comments. New tests directly cover the regression (asserting both that the hint appears and that the generic "Bun could not find a file" fallback does not), and an existing test was updated rather than left stale. The change is small, mechanical, and well-commented.
There was a problem hiding this comment.
LGTM — focused error-name mapping that restores the existing cwd-deleted hint, with tests.
Extended reasoning...
Overview
This PR restores the actionable "current working directory was deleted" message when bun starts from an unlinked cwd. It touches three Rust files (src/bun_core/util.rs, src/sys/Error.rs, src/sys/lib.rs) to map getcwd+ENOENT to the interned error name CurrentWorkingDirectoryUnlinked, which crash_handler::handle_root_error already matches on (verified at src/crash_handler/lib.rs:1341). The From<bun_sys::Error> impl now routes through to_zig_err(), whose fallthrough is identical to the old from_errno call, so non-getcwd errors are unaffected. Two test files are updated: an existing compiled-binary deleted-cwd assertion is tightened, and a new POSIX-only suite covers bun -e / bun run.
Security risks
None. This is purely an errno→error-name refinement on an existing failure path; no new inputs, no auth/crypto/permissions, no externally controllable data.
Level of scrutiny
Low. The change is small (~20 LOC of logic), mechanical, and mirrors the documented Zig stdlib behavior it was ported from. The target error name and consumer already exist in the codebase — this just wires up the producer that went missing during the Zig→Rust port. Worst-case regression would be a slightly different error string on a startup failure path that already exits 1.
Other factors
The bug-hunting system found no issues. Test coverage is solid: the new tests assert both the presence of the hint and the absence of the generic fallback, and the existing compile test was updated rather than left stale. No outstanding human review comments on the PR.
What
Restore the actionable "The current working directory was deleted..." hint when bun is started from a directory that no longer exists.
Repro
Before:
After:
Cause
crash_handler::handle_root_errorhas an arm forbun_core::err!("CurrentWorkingDirectoryUnlinked")(ported from the Zigerror.CurrentWorkingDirectoryUnlinkedcase), but nothing ever produced that name. Zig'sstd.posix.getcwdmapsENOENTtoerror.CurrentWorkingDirectoryUnlinkedat the syscall wrapper; the Rustbun_sys::getcwdreturns a plainbun_sys::Error{errno: ENOENT, syscall: Tag::getcwd}, and bothFrom<bun_sys::Error> for bun_core::Errorandbun_sys::Error::to_zig_err()discarded the syscall tag and interned the bare errno name"ENOENT".bun_core::getcwdwent throughstd::io::Errorwith the same result. The error then fell through to the generic ENOENT arm inhandle_root_error.Fix
bun_sys::Error::to_zig_err: whensyscall == Tag::getcwd && errno == ENOENT, returnerr!("CurrentWorkingDirectoryUnlinked").From<bun_sys::Error> for bun_core::Error: route throughto_zig_err()so?conversions pick up the same mapping.bun_core::getcwd(which callslibc::getcwddirectly and bypassesbun_sys::Error): check forENOENTand return the named error.Verification
New tests in
test/cli/run/run-crash-handler.test.tscoverbun -eandbun runfrom a deleted cwd (POSIX only; Windows refuses to remove a process's cwd). Updated the existing compiled-binary test intest/bundler/bun-build-compile.test.tswhich routes through the samehandle_root_errorpath and now sees the better message.