getcwd: report CurrentWorkingDirectoryUnlinked from a deleted cwd again
Merged
RO
3mo ago+50+50+01234567890123456789-9-9-0123456789
Problem
- Starting bun from a directory that has been deleted prints the generic fallback instead of the hint that tells the user what to do:
ENOENT: Bun could not find a file, and the code that produces this error is missing a better error. crash_handler::handle_root_error(src/crash_handler/lib.rs) already has an arm for the error nameCurrentWorkingDirectoryUnlinkedthat printsThe current working directory was deleted, so that command didn't work. Please cd into a different directory and try again., but nothing produces that name, so the arm is dead code.- Cause: before the port,
std.posix.getcwdmappedENOENTtoerror.CurrentWorkingDirectoryUnlinked. The call sites that feedhandle_root_errornow usebun_sys::getcwd, whose error collapses to a bareSystemErrno::ENOENT, andbun_core's owngetcwdreturnedUnexpected. - Repro (POSIX):
mkdir /tmp/gone && cd /tmp/gone && rmdir /tmp/gone && bun install(orbun test, or abun build --compilebinary).bun run/bun -eare unaffected: they fall back to the executable's directory and boot.
Fix
bun_core::Errorgains aCurrentWorkingDirectoryUnlinkedvariant; itsname()is the stringhandle_root_errormatches on.bun_core'sgetcwd_lenreturns that variant whenerrno == ENOENT, makingbun_core::getcwdthe one producer of the name, asstd.posix.getcwdwas before.- The three
getcwdcalls whose failure propagates tohandle_root_errornow callbun_core::getcwdinstead ofbun_sys::getcwd: the--cwdbase and the default cwd inArguments::parse(CLI commands), andFileSystem::initin the resolver (compiled binaries). Every otherbun_sys::getcwdcaller is unchanged, andbun_sys::Erroritself is untouched. - Verified by
test/cli/run/run-crash-handler.test.ts(bun installandbun testfrom a deleted cwd print the hint;bun -eboots via the exe-dir fallback) and the existing compiled-binary case intest/bundler/bun-build-compile.test.ts, which now asserts the hint. With the foursrc/files reverted to the merge base, the three hint assertions fail (generic ENOENT message); with them applied, all pass.
Background
handle_root_erroris where anErrreturned from bun'smainends up. It takes anything implementingErrNameand switches on the error's name string to pick a user-facing message, falling back to a generic one.- Each crate has its own
thiserrorenum with aCore(bun_core::Error)variant, andname()on a wrapper delegates to the inner error, so abun_corevariant returned at the bottom of the chain (bun_core::getcwd-> resolver -> bundler -> jsc -> runtime) surfaces its name unchanged at the top. bun_core::getcwdandbun_sys::getcwdare two wrappers over the same libc call; the difference is the error type.bun_sys's returns the rich syscall error, which other callers want for JS-visible errors;bun_core's returns abun_core::Error, which is what the startup paths need here.
Earlier shape of this PR
The first version added a bun_sys::Error::to_named_core_err() helper holding the (getcwd, ENOENT) mapping and consulted it from the From<bun_sys::Error> impls in bun_runtime and bun_resolver. 4c99108 replaced that with switching the three call sites to bun_core::getcwd, which keeps the mapping in one place and leaves bun_sys and the From impls as they were.