Skip to content

install(bin): support cross-volume Windows global installs - #30154

Closed
robobun wants to merge 5 commits into
mainfrom
farm/7d5943e7/windows-cross-drive-shim
Closed

install(bin): support cross-volume Windows global installs#30154
robobun wants to merge 5 commits into
mainfrom
farm/7d5943e7/windows-cross-drive-shim

Conversation

@robobun

@robobun robobun commented May 3, 2026

Copy link
Copy Markdown
Collaborator

Fixes #30129

Repro

On Windows, with the global bin directory on a different physical drive than the global package store:

$env:BUN_INSTALL_BIN = "E:\Packages\bun\bin"
$env:BUN_INSTALL_CACHE_DIR = "E:\Packages\bun\cache"
Remove-Item Env:BUN_INSTALL -ErrorAction SilentlyContinue
Remove-Item Env:BUN_INSTALL_GLOBAL_DIR -ErrorAction SilentlyContinue

bun install -g github:b-nnett/codex-plusplus#0.1.4

Panics with:

panic(main thread): Internal assertion failure at install\bin.zig:738:83

…leaving a truncated empty .bunx behind. Microsoft's Dev Drive docs specifically call out this layout — package caches on a Dev Drive, launcher shims on PATH elsewhere.

Cause

createWindowsShim computes a relative path from the shim's .bin directory to the target binary and asserts it starts with ..\:

const rel_target = path.relativeBufZ(this.rel_buf, path.dirname(abs_dest, .auto), abs_target);
bun.assertWithLocation(strings.hasPrefixComptime(rel_target, "..\\"), @src());

Windows cannot express a relative path between two absolute paths on different volumes, so relativeBufZ falls through to returning the absolute target (C:\Users\…\cli.js), hasPrefixComptime is false, and the assertion fires. The .bunx file has already been opened with O_TRUNC, so an empty file is left in the bin directory.

The same assertion family also fires on POSIX (createSymlink) and on Windows for the zero-.. shape — a package whose bin field resolves inside the sibling .bin directory.

Fix

Anchor the stored bin_path to the parent of the .bin directory — which is where bun_shim_impl.exe's walk-back loop actually lands at runtime — instead of to .bin itself. Two shapes then Just Work without stripping a ..\ prefix:

  • common case: some-pkg\\dist\\cli.js (no ..)
  • zero-.. case: .bin\\foo.js (target lives inside the bin dir)

For truly cross-volume setups, relativeBufZ returns the absolute target. Encode it directly and set a new is_absolute_target flag in the shim header:

  • VersionFlag bumped to v6
  • Flags packed struct grew from u16 to u32 to make room
  • The launcher relocates the metadata to start at buf1_u8[2 * nt_object_prefix.len] when the flag is set, so the subsequent decode path sees an absolute path at the same offset the relative case would have produced — no further branching in the command-line construction.

Also drop the equivalent POSIX assertion in createSymlink: symlink(2) is happy with any relative string.

Verification

test/regression/issue/30129.test.ts installs a local package whose bin resolves into the sibling .bin directory, producing the same zero-.. shape that fires the assertion on both POSIX (createSymlink) and Windows (createWindowsShim). Runs on Linux and Windows CI:

  • Without the fix: bun install panics during bin linking and exits non-zero before node_modules artefacts land on disk.
  • With the fix: the install completes and the bin-link artefact is produced (symlink foo.js on POSIX, .exe + .bunx pair on Windows). The Windows branch additionally decodes the .bunx header and asserts bin_path equals .bin\\foo.js — the parent-of-.bin-anchored form — so a regression that stored the wrong relative form would be caught.

Gate proof (Linux, bun bd):

$ git stash push -- src/ && bun bd test test/regression/issue/30129.test.ts
0 pass, 1 fail  # panic at install/bin.zig during bin linking
$ git stash pop && bun bd test test/regression/issue/30129.test.ts
1 pass, 0 fail

Related

@coderabbitai

coderabbitai Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

Warning

Rate limit exceeded

@robobun has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 6 minutes and 12 seconds before requesting another review.

To keep reviews running without waiting, you can enable usage-based add-on for your organization. This allows additional reviews beyond the hourly cap. Account admins can enable it under billing.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 44c2832e-e6a0-492a-a9aa-72aeee4f5792

📥 Commits

Reviewing files that changed from the base of the PR and between 1367f2a and b83cee5.

📒 Files selected for processing (1)
  • test/regression/issue/30129.test.ts

Walkthrough

Windows bin shims now support absolute/cross-volume targets via a new is_absolute_target metadata flag and a shim format bump to v6; creation, encoding, and decoding logic updated accordingly and a regression test covers cross-drive and non-.. relative-target cases.

Cross-Volume Windows Bin Shim Support

Layer / File(s) Summary
Shim Format / Data Shape
src/install/windows-shim/BinLinkingShim.zig
Format version advanced v5 → v6; Flags sized u16u32; VersionFlag widened; new field is_absolute_target: bool = false added; flag validation and encoding updated.
Shim Creation / Wiring
src/install/bin.zig
createWindowsShim computes bin_path_w and is_absolute_target from the relative result (handles ..\\, absolute Windows paths, and non-.. local forms), passes is_absolute_target into WinBinLinkingShim, and uses bin_path_w for shebang parsing.
Symlink behavior
src/install/bin.zig
createSymlink removes the assertion that relative targets must start with .. and documents that symlink(2) accepts other relative forms (including bare filenames).
Shim Decoding & Buffer Relocation
src/install/windows-shim/bun_shim_impl.zig
Adds bounds check ensuring read contains Flags; for flags.is_absolute_target, relocates metadata bytes within the read buffer using forward-copy to handle overlap, re-anchors read_ptr, verifies relocated Flags decode identically, and adjusts metadata-size validation to 4 + 8 + @sizeof(Flags).
Tests
test/regression/issue/30129.test.ts
Adds regression test exercising install when a package bin resolves to a non-.. relative target (pre-creating .bin/foo.js), asserting install succeeds, artifacts extracted, lockfile written, and platform-specific bin-link outputs (Windows .bunx contents, non-Windows symlink) are correct.
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely summarizes the primary change: enabling cross-volume Windows global installs by fixing the bin directory assertion.
Description check ✅ Passed The description comprehensively covers the repro, root cause, fix approach, and verification. It exceeds the template with detailed technical context and related issues.
Linked Issues check ✅ Passed The code changes fully address issue #30129: they support cross-volume Windows installs via bin_path anchoring, absolute-target fallback with is_absolute_target flag, VersionFlag/Flags updates, launcher metadata relocation, POSIX assertion removal, and regression test coverage.
Out of Scope Changes check ✅ Passed All changes align with the linked issue objectives: Windows shim format/launcher updates, assertion fixes, test coverage, and related issue superseding. No extraneous modifications detected.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.


Review rate limit: 0/5 reviews remaining, refill in 6 minutes and 12 seconds.

Comment @coderabbitai help to get the list of available commands and usage tips.

@robobun

robobun commented May 3, 2026

Copy link
Copy Markdown
Collaborator Author
Updated 11:05 PM PT - May 4th, 2026

@robobun, your commit 4551282 has 2 failures in Build #51396 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 30154

That installs a local version of the PR into your bun-30154 executable, so you can run:

bun-30154 --bun

@github-actions

github-actions Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

Found 2 issues this PR may fix:

  1. Bug: Windows global binaries (.exe/.bunx) resolve incorrect node_modules path when Bun is installed via Scoop #28891 - .bunx shims contain hardcoded relative paths that resolve incorrectly when Scoop bin dir and global install dir are in different locations; the absolute-target fallback would fix this
  2. windows temp path is wrongly recognized (bunx) #13083 - .bunx files produce malformed mixed relative/absolute paths when %TEMP% is on a different drive letter; same cross-volume relative path bug this PR addresses

If this is helpful, copy the block below into the PR description to auto-close these issues on merge.

Fixes #28891
Fixes #13083

🤖 Generated with Claude Code

@github-actions

github-actions Bot commented May 3, 2026

Copy link
Copy Markdown
Contributor

This PR may be a duplicate of:

  1. install: support Windows global bin shims across drives #30130 - Fixes the same issues (Windows global install panics when BUN_INSTALL_BIN and global package store are on different drives #30129, Bun crashes when trying to install gemini-cli (windows bin linking assertion fail) #23414, All Global Installations Fail #29005) with the same cross-volume Windows global bin shim approach, touching the same three files
  2. install: remove overly strict bin linking assertions #29011 - Removes the same overly strict bin linking assertions in createWindowsShim and createSymlink that cause the cross-volume panic (fixes All Global Installations Fail #29005)
  3. fix(install): remove overly strict bin linking assertions #28225 - Removes the same two assertions in createWindowsShim and createSymlink at bin.zig that install(bin): support cross-volume Windows global installs #30154 addresses

🤖 Generated with Claude Code

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/install/windows-shim/BinLinkingShim.zig (1)

33-52: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Avoid publishing v6 metadata before the matching launcher is guaranteed to land.

Line 34 bumps the shim format to v6, and Lines 56-87 change the footer layout to u32, but src/install/bin.zig still truncates/writes .bunx first and then ignores error.EBUSY if rewriting the sibling .exe fails. That can leave an old v5 launcher next to a v6 .bunx, after which direct launches of that global bin will fail until something rewrites the .exe. Please either make the update transactional or keep the new launcher backward-compatible before publishing v6 metadata.

Also applies to: 56-87

🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@src/install/windows-shim/BinLinkingShim.zig` around lines 33 - 52,
VersionFlag was incremented to v6 and footer layout changed to u32 while
bin-writing logic still writes the .bunx first and swallows error.EBUSY when
updating the sibling .exe, which can leave mismatched v5/.bunx v6 pairs; either
make the install update transactional or keep v6 metadata out until launchers
exist. Fix by updating the writer in src/install/bin.zig: implement atomic
replacement (write both .bunx and .exe to temp files, fsync, then rename into
place) or change order to create/replace the .exe first and only replace the
.bunx if the .exe update succeeds, and ensure error.EBUSY on the second write
triggers rollback of the first (delete temp or restore previous file) so no
mismatched pair remains; alternatively revert the VersionFlag bump (remove v6
current) or retain backward-compatible footer parsing until the new launcher is
published. Ensure references: VersionFlag (v6), footer u32 change, and the
bin-writing logic in src/install/bin.zig are updated accordingly.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@src/install/windows-shim/bun_shim_impl.zig`:
- Around line 590-605: The bounds check uses buf1.len (count of u16s) instead of
byte capacity, causing false rejections when relocating metadata in
flags.is_absolute_target; update the comparison to use byte length (buf1_u8.len)
or equivalently buf1.len * `@sizeOf`(u16)) so the condition becomes if (read_len >
buf1_u8.len - new_offset) before returning mode.fail(.InvalidShimDataSize) —
adjust the check around new_offset/read_len in the relocation block to compare
bytes, not u16 count.

---

Outside diff comments:
In `@src/install/windows-shim/BinLinkingShim.zig`:
- Around line 33-52: VersionFlag was incremented to v6 and footer layout changed
to u32 while bin-writing logic still writes the .bunx first and swallows
error.EBUSY when updating the sibling .exe, which can leave mismatched v5/.bunx
v6 pairs; either make the install update transactional or keep v6 metadata out
until launchers exist. Fix by updating the writer in src/install/bin.zig:
implement atomic replacement (write both .bunx and .exe to temp files, fsync,
then rename into place) or change order to create/replace the .exe first and
only replace the .bunx if the .exe update succeeds, and ensure error.EBUSY on
the second write triggers rollback of the first (delete temp or restore previous
file) so no mismatched pair remains; alternatively revert the VersionFlag bump
(remove v6 current) or retain backward-compatible footer parsing until the new
launcher is published. Ensure references: VersionFlag (v6), footer u32 change,
and the bin-writing logic in src/install/bin.zig are updated accordingly.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: ade1b510-0550-4e3b-a194-d305974e3827

📥 Commits

Reviewing files that changed from the base of the PR and between 9615455 and a95a49a.

📒 Files selected for processing (4)
  • src/install/bin.zig
  • src/install/windows-shim/BinLinkingShim.zig
  • src/install/windows-shim/bun_shim_impl.zig
  • test/regression/issue/30129.test.ts

Comment thread src/install/windows-shim/bun_shim_impl.zig
Comment thread src/install/bin.zig Outdated
Comment thread test/regression/issue/30129.test.ts Outdated
Comment thread src/install/bin.zig Outdated

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.

Inline comments:
In `@test/regression/issue/30129.test.ts`:
- Around line 44-53: The test currently spawns Bun (proc via Bun.spawn with cmd
[bunExe(), "install"]) and collects outputs but discards stderr; update the
assertion after awaiting Promise.all([proc.stdout.text(), proc.stderr.text(),
proc.exited]) to assert exitCode === 0 and that the captured stderr string does
not include the substring "error:" (use the variable bound to proc.stderr.text()
— e.g., stderr) so install regressions fail if Bun emitted its error marker;
apply the same change to the similar occurrence at the other location referenced
(line ~93).
- Around line 73-88: Add a new Windows-only test that directly exercises the
is_absolute_target path by constructing a .bunx shim whose rel_target_from_bin
is an absolute path and verifying the shim decoding logic in bun_shim_impl.zig
yields that absolute path; specifically, on Windows create a .bunx file
containing the UTF-16LE-encoded shim header with an absolute target (e.g.,
"C:\\path\\to\\foo.js"), then read it with the same
TextDecoder("utf-16le")/terminator logic used in the existing test and assert
the decoded slice equals the absolute path string so the is_absolute_target
branch is covered.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: cfdc5e45-ac7b-4161-a86f-16fb279651f5

📥 Commits

Reviewing files that changed from the base of the PR and between a95a49a and 1367f2a.

📒 Files selected for processing (3)
  • src/install/bin.zig
  • src/install/windows-shim/bun_shim_impl.zig
  • test/regression/issue/30129.test.ts

Comment thread test/regression/issue/30129.test.ts
Comment thread test/regression/issue/30129.test.ts
@robobun

robobun commented May 3, 2026

Copy link
Copy Markdown
Collaborator Author

CI x64-asan test-bun shard failed on a known pre-existing flake in test/js/web/fetch/fetch-http2-client.test.tsASSERTION FAILED: wasRemoved in vendor/WebKit/Source/WTF/wtf/text/AtomStringImpl.cpp on one test and ThreadLock locked by wrong thread on another. Both in HTTP/2 fetch code paths under ASAN, unrelated to any file this PR touches.

Confirmed as a pre-existing flake: build #50489 on an unrelated branch (farm/7b9bd2b4/h2-flushqueue-rst-reentry) failed with the exact same AtomStringImpl::remove wasRemoved assertion. The recent WebKit bump in #30096 (commit bff04ad2c0) is a plausible trigger but not something this PR can fix.

The rest of the test-bun shards are still in progress (72/287 jobs completed, 213 waiting). I'll let them run — requesting a retry on the known-flaky asan job.

@robobun

robobun commented May 3, 2026

Copy link
Copy Markdown
Collaborator Author

Update: 278/287 test-bun shards now passed. Two flakes remain, both pre-existing on unrelated test files:

  1. test/js/web/fetch/fetch-http2-client.test.ts on debian 13 x64-asanASSERTION FAILED: wasRemoved in vendor/WebKit/Source/WTF/wtf/text/AtomStringImpl.cpp and a separate ThreadLock panic. Same flake that hit build #50489 on farm/7b9bd2b4/h2-flushqueue-rst-reentry.
  2. test/bake/dev-and-prod.test.ts: hmr handles rapid consecutive edits on windows 2019 x64. Known flaky on Windows — at least one of 5 windows x64 shards has failed on every recent build I checked (50489, 50493, 50497, 50501) across unrelated branches.

Worth flagging: the Windows shard that did run my test passed it cleanly — 1 pass 0 fail 9 expect() calls on test/regression/issue/30129.test.ts. 9 expect calls is the Windows branch (if (isWindows)) of the test, which includes decoding the .bunx header and asserting bin_path equals .bin\foo.js. That directly confirms the fix works end-to-end on Windows for the zero-.. shape this PR addresses, at least.

Neither flake touches files this PR modifies (src/install/bin.zig, src/install/windows-shim/*). Waiting on the remaining 9 shards; requesting a retry on the two flaky jobs.

@robobun

robobun commented May 3, 2026

Copy link
Copy Markdown
Collaborator Author

Final CI state for build 50504 (no more retries remaining):

  • 280 test-bun shards passed, including both shards that ran this PR's test (test/regression/issue/30129.test.ts): Linux aarch64 (5 expect calls, POSIX branch) and Windows x64 (9 expect calls, Windows-gated branch including .bunx header decode).
  • 2 failed, both pre-existing flakes on files this PR doesn't touch:
    • debian 13 x64-asan: test/js/web/fetch/fetch-http2-client.test.ts hit ASSERTION FAILED: wasRemoved in vendor/WebKit/Source/WTF/wtf/text/AtomStringImpl.cpp on one test and ThreadLock panic on another. Same WebKit ASAN flake that failed build #50489 on an unrelated branch (farm/7b9bd2b4/h2-flushqueue-rst-reentry).
    • windows 2019 x64: test/bake/dev-and-prod.test.ts: hmr handles rapid consecutive edits timed out after 15s across 5 retry attempts. At least one of 5 recent builds I spot-checked (50489, 50493, 50497, 50501) has this same shard fail.
  • 💤 2 expired macOS shards (darwin-14-aarch64, darwin-26-aarch64) — never got an agent. Infra, not test failure.

No action this PR can take — both genuine failures are in tests for HTTP/2 fetch + bake dev server HMR, on ASAN and Windows specifically. Would appreciate a maintainer retrying the two flaky test-bun jobs (the retry button for debian 13 x64-asan - test-bun and windows 2019 x64 - test-bun).

robobun and others added 4 commits May 4, 2026 10:23
Fixes #30129

On Windows, bun install -g panicked at install/bin.zig:738 with
'Internal assertion failure' when BUN_INSTALL_BIN lived on a
different physical drive from the global package store. The
assertion checked that path.relative from the shim's .bin directory
to the target binary starts with '..\\' — Windows cannot produce
such a walk between two volumes, so relativeBufZ fell through to
returning the absolute target and the assertion fired. The .bunx
file had already been opened with O_TRUNC, so a corrupted empty
.bunx was left behind.

This is the supported split-storage case called out in Microsoft's
Dev Drive docs: package caches and repositories on a non-system
Dev Drive, launcher/tool shims on PATH on a separate drive.

Fix: anchor the stored bin_path to the parent of the .bin directory
— matching where bun_shim_impl.exe's walk-back loop actually lands
at runtime — instead of the .bin directory itself. That handles
two shapes naturally without stripping a '..\\' prefix:

  - common case: relative path is 'some-pkg\\dist\\cli.js' (no '..')
  - zero-'..' case: target lives inside .bin, relative is
    '.bin\\foo.js'

For truly cross-volume setups, relativeBufZ returns the absolute
target. Encode it directly and set a new is_absolute_target flag in
the shim header (VersionFlag bumped to v6, Flags packed struct grew
from u16 to u32 to make room). The launcher relocates the metadata
to start at buf1_u8[2 * nt_object_prefix.len] when the flag is set,
so the subsequent decode path sees an absolute path at the same
offset the relative case would have produced — no further
branching needed in the command-line construction.

Also drop the equivalent non-Windows assertion in createSymlink:
symlink(2) is perfectly happy with any relative string, including a
bare filename when the target resolves inside abs_dest_dir itself.

Verification: test/regression/issue/30129.test.ts installs a local
package whose bin resolves into the sibling .bin directory, producing
the same zero-'..' shape that fires the assertion on both POSIX
(createSymlink) and Windows (createWindowsShim). Without the fix,
bun install panics during bin linking and exits non-zero. The Windows
branch additionally decodes the .bunx header and asserts bin_path
equals '.bin\\foo.js'.
claude[bot] caught a regression: when BUN_INSTALL_BIN is one level
below a drive root (e.g. E:\bin), path.dirname(path.dirname("E:\bin"))
returns "E:" without a trailing \, which isAbsoluteWindows treats
as drive-relative. path.relative then silently resolves against
top_level_dir instead of E:\, encoding the wrong path. The bug's
repro (E:\Packages\bun\bin) hides this because it's 3 levels deep.

Switch the anchor computation so it doesn't depend on two-level
dirname: compute rel_target relative to <bin_dir> (single dirname,
always absolute), then shape based on the result:

  - Starts with '..\\': common case — strip the single '..\\' (the
    launcher walks back one level past <bin_dir>).
  - Absolute: cross-volume — flag as is_absolute_target.
  - Otherwise: zero-'..' shape (target inside <bin_dir> itself) —
    prepend basename(<bin_dir>) + '\\' so the launcher's
    post-walk-back concatenation lands back inside the bin directory.

This handles E:\bin, C:\Users\foo\bin, cross-drive, and the
inside-'.bin' zero-'..' case all through the same code path.

Also address three review nits in the same push:

- createSymlink doc comment: use forward-slash example (this function
  is POSIX-only; the previous '..\\..\\package\\bin.js' example was
  misleading).
- bun_shim_impl bounds check: clarify the byte-vs-u16 equivalence
  with an explicit buf1_u8_len alias, per coderabbit.
- Test: drop the expect(stderr).not.toContain("Internal assertion
  failure") line. assertWithLocation compiles out in release, so
  the check is tautological in CI release lanes; the exitCode and
  lockfile-existence assertions already gate the regression
  equivalently (and catch it in debug builds too).
coderabbit suggestion. Distinct from the debug-only 'Internal assertion
failure' check I removed earlier — 'error:' is Bun's user-facing error
marker that appears in both debug and release lanes. Without the fix,
if assertions are compiled out (release), bin-linking can still fail
and surface an 'error:' line even though the process doesn't panic.
@Jarred-Sumner
Jarred-Sumner force-pushed the farm/7d5943e7/windows-cross-drive-shim branch from b83cee5 to 1fe22e8 Compare May 4, 2026 10:23
… jobs across darwin, windows, alpine, freebsd)

@claude claude Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I didn't find any new issues and all prior feedback is resolved, but this bumps the .bunx shim format (v5→v6, Flags u16→u32) and adds buffer-relocation pointer arithmetic in bun_shim_impl.zig whose is_absolute_target branch can't be exercised in CI — worth a human pass on the launcher changes and format-bump compat before merging.

Extended reasoning...

Overview

Touches four files: src/install/bin.zig (rewrites createWindowsShim's bin-path computation into a three-way shape dispatch and drops the POSIX createSymlink assertion), src/install/windows-shim/BinLinkingShim.zig (format bump v5→v6, Flags packed struct widened u16→u32, new is_absolute_target field), src/install/windows-shim/bun_shim_impl.zig (new metadata-relocation branch with copyForwards over overlapping ranges, re-anchored read_ptr, recomputed validation_length_offset), and a new regression test.

Security risks

None identified. Inputs to the changed code are filesystem paths derived from the install layout, not attacker-controlled network/user data. The shim launcher already validates flags/lengths before acting; the new branch adds a defensive bounds check before the relocation copy.

Level of scrutiny

High. This is a binary-format version bump for the Windows .bunx shim plus hand-tuned pointer arithmetic in a launcher that runs for every globally-installed bin on Windows. The Flags size change means the new launcher reads 4 trailing bytes instead of 2, so any pre-existing v5 .bunx decodes as invalid under the new code (handled by the existing isValid() fallback in non-standalone mode, but the standalone .exe is co-written so should stay paired). The author and reviewers explicitly agree the is_absolute_target relocation path in bun_shim_impl.zig cannot be exercised on current CI without a real multi-drive topology. The file header also directs questions to a specific maintainer.

Other factors

All seven prior inline review threads (mine and CodeRabbit's) are resolved; the drive-root E:\\bin regression I flagged was fixed by switching to single-dirname + shape dispatch. CI is green on the shards that ran the new test (Linux POSIX branch and Windows .bunx-decode branch both passed); remaining failures are documented pre-existing flakes in unrelated HTTP/2 and bake HMR tests. The change is well-reasoned and likely correct, but the combination of format bump + untested launcher branch + intricate buffer-offset logic is beyond what I'm comfortable auto-approving.

@robobun

robobun commented May 4, 2026

Copy link
Copy Markdown
Collaborator Author

Update on the retrigger build (51396):

  • 3 failed Windows shards, same flake in every one: test/js/bun/test/parallel/test-http-should-emit-close-when-connection-is-aborted.ts — timed out 4× across retries. Same test hit build #51385 on an unrelated branch (farm/ae54c7f9/dns-libinfo-start-error-test) with the identical 4-retry timeout pattern. Pre-existing Windows flake in HTTP connection-close test, not this PR.
  • Rest of the build is healthy (agent pool recovered since 50974) and my test/regression/issue/30129.test.ts will run once the non-broken shards reach it.

Re: @claude[bot]'s heads-up on the launcher branch being exercisable only via real multi-drive topology — agreed and flagged in a prior thread. The is_absolute_target relocation path in bun_shim_impl.zig can't be exercised by CI as-configured; the zero-.. Windows shard confirms the encoder side writes correct .bunx bytes (.bin\foo.js decoded from the header), and the POSIX Linux shard confirms the non-absolute path through createSymlink. A human pass on bun_shim_impl.zig:590-613 (the flags.is_absolute_target block with std.mem.copyForwards over potentially-overlapping ranges) and the Flags u16→u32 wire-format bump is sensible before merge — both are described in inline comments and the PR body.

@robobun

robobun commented Jun 26, 2026

Copy link
Copy Markdown
Collaborator Author

Closing: this PR's implementation lives entirely in Zig source files that have since been removed from the tree as part of the Rust migration. The change can no longer merge cleanly and the files it edits no longer exist on main.

If the underlying issue is still present, it will need a fresh fix against the Rust implementation.

@robobun robobun closed this Jun 26, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Windows global install panics when BUN_INSTALL_BIN and global package store are on different drives

1 participant