Fix resolver panic on Windows drive-letter specifiers on POSIX - #32017
Fix resolver panic on Windows drive-letter specifiers on POSIX#32017robobun wants to merge 3 commits into
Conversation
…POSIX
On POSIX hosts a specifier like "C:/" is not an absolute path, so the
resolver treats it as a bare package specifier. The loose-platform join
used for the node_modules search, tsconfig baseUrl/paths, and
package.json field joins still recognized "C:/" as Windows-absolute and
adopted it as the join root, returning a path that is not absolute on
the host. That tripped the assert in dir_info_cached:
panic: cannot resolve DirInfo for non-absolute path: C:/
In the POSIX branch of _join_abs_string_buf, a part now resets the join
root only when it starts with a path separator. Drive-letter parts join
as ordinary relative components, so resolution fails with a catchable
"Cannot find module" error, and a directory literally named "C:" under
node_modules remains resolvable, matching Node's CJS loader.
Fixes #32016
|
Updated 3:05 AM PT - Jun 10th, 2026
❌ @robobun, your commit 612353c has some failures in 🧪 To try this PR locally: bunx bun-pr 32017That installs a local version of the PR into your bun-32017 --bun |
|
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)
WalkthroughThis PR fixes a panic in ChangesDrive-letter path resolution
Possibly related issues
🚥 Pre-merge checks | ✅ 4✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Comment |
|
CI status for the latest build (61695, after main was merged in with the #32042 bunx skip): 284 jobs passed with zero test failures, including this PR's new tests on every lane that ran (all Linux glibc/musl/baseline/asan, all Windows, macOS 26 aarch64 and 14 x64). The only red is the two darwin-14-aarch64 test jobs, which expired in queue without ever running (agent capacity; the same pool stalled the previous build). Nothing left to fix in the diff; those two jobs just need a retry from the Buildkite UI once agents are available. |
|
Status check against current main (165dc9f):
So this PR is no longer needed for the crash; what remains is the drive-letter-as-bare-specifier join behavior. The src hunk still applies cleanly to main. Leaving open for a maintainer to decide whether that behavior is wanted on its own. |
Fixes #32016
Repro
On Linux or macOS, with a
node_modulesdirectory in cwd or any ancestor (or a tsconfig.json withbaseUrl):The same panic is reachable through
require.resolve("C:/"),Bun.resolveSync("C:/", dir), and dynamicimport("C:/"), with anyX:/orX:\shaped specifier.Cause
On POSIX,
C:/is not an absolute path, so the resolver correctly classifies it as a bare package specifier and enters the node_modules search, joining[dir, "node_modules", import_path]with the loose-platform join. The loose platform'sis_absoluteaccepts Windows drive paths on any host, so the join's reset loop in_join_abs_string_buf(src/paths/resolve_path.rs) adoptedC:/as a new join root and returned it verbatim.dir_info_cachedthen asserts host-native absoluteness (src/resolver/resolver.rs:4194) and panics. The assert is a plainassert!, so release builds crash too, making any import specifier shaped like a drive path a DoS.The tsconfig
baseUrl/pathsjoins and the package.jsonmain/browserfield joins funnel through the same primitive and panic the same way (import("C:/nope")with"baseUrl": "."reproduces it without any node_modules).Fix
In the POSIX branch of
_join_abs_string_buf, a part resets the join root only when it starts with a path separator. A drive-letter part cannot be a usable root on a POSIX host (the result would not be absolute, violating the join's contract), so it now joins as an ordinary relative component. Resolution then fails with a catchable "Cannot find module" error, exactly like the no-node_modules case, and a directory literally namedC:under node_modules is findable, matching Node's CJS loader (path.resolve(nmDir, "C:/x")lands inside node_modules).The Windows-host join is dispatched to
_join_abs_string_buf_windowsbefore this loop and is unchanged; drive letters are real roots there. POSIX-absolute parts (env-derived cache dirs, NODE_PATH entries, absolute specifiers) still reset as before.Verification
Two tests added to
test/js/bun/resolve/resolve-error.test.ts(POSIX-only; drive paths are genuinely absolute on Windows):import.meta.resolve/require.resolve/Bun.resolveSync/import()forC:/,C:\,D:\foo,c:/xall throwERR_MODULE_NOT_FOUND/MODULE_NOT_FOUND, plus a positive case resolvingnode_modules/C:/real.jsimport("C:/nope")with"baseUrl": "."throws instead of panickingBoth tests panic the child process on the unfixed build (
USE_SYSTEM_BUN=1) and pass with the fix. The fulltest/js/bun/resolve/suite,test/bundler/esbuild/tsconfig.test.ts,test/bundler/esbuild/packagejson.test.ts, andtest/bundler/resolver/pass (the only failures are three pre-existing stress-test timeouts that fail identically without this diff).