diff --git a/src/install/bin.zig b/src/install/bin.zig index 9d0a5e4e0c..1ac5d14789 100644 --- a/src/install/bin.zig +++ b/src/install/bin.zig @@ -720,9 +720,15 @@ pub const Bin = extern struct { defer bunx_file.close(); const rel_target = path.relativeBufZ(this.rel_buf, path.dirname(abs_dest, .auto), abs_target); - bun.assertWithLocation(strings.hasPrefixComptime(rel_target, "..\\"), @src()); - const rel_target_w = strings.toWPathNormalized(&target_buf, rel_target["..\\".len..]); + // For Windows shims, we need a path relative to node_modules, not relative to .bin + // Most cases will start with "..\\" to go up from .bin, but edge cases may differ + const rel_target_for_shim = if (strings.hasPrefixComptime(rel_target, "..\\")) + rel_target["..\\".len..] + else + rel_target; + + const rel_target_w = strings.toWPathNormalized(&target_buf, rel_target_for_shim); const shebang = shebang: { const first_content_chunk = contents: { diff --git a/test/regression/issue/23414.test.ts b/test/regression/issue/23414.test.ts new file mode 100644 index 0000000000..b864b4dcf7 --- /dev/null +++ b/test/regression/issue/23414.test.ts @@ -0,0 +1,44 @@ +// https://github.com/oven-sh/bun/issues/23414 +// Bun crashes when trying to install packages with bin links on Windows +// This was caused by an overly strict assertion that expected all relative paths +// to start with "..\\" when linking bins, but edge cases exist where this isn't true. + +import { expect, test } from "bun:test"; +import { bunEnv, bunExe, isWindows, tempDir } from "harness"; + +test("bin linking should not crash with assertion failure on Windows", async () => { + if (!isWindows) { + // This test is Windows-specific, skip on other platforms + return; + } + + using dir = tempDir("bin-linking-test", { + "package.json": JSON.stringify({ + name: "test-package", + version: "1.0.0", + dependencies: { + "cowsay": "1.6.0", + }, + }), + }); + + await using proc = Bun.spawn({ + cmd: [bunExe(), "install"], + env: bunEnv, + cwd: String(dir), + stderr: "pipe", + stdout: "pipe", + }); + + const [stdout, stderr, exitCode] = await Promise.all([proc.stdout.text(), proc.stderr.text(), proc.exited]); + + // The main fix is that this should not panic with: + // "panic: Internal assertion failure at install/bin.zig:723:83" + expect(stderr).not.toContain("panic"); + expect(stderr).not.toContain("assertion failure"); + expect(stderr).not.toContain("bin.zig:723"); + + // Verify the install completed successfully + expect(exitCode).toBe(0); + expect(stderr).toContain("package installed"); +});