Skip to content

fix(install): skip env flags like -S when parsing shebangs on Windows#28127

Closed
robobun wants to merge 4 commits intomainfrom
claude/fix-env-s-shebang-28126
Closed

fix(install): skip env flags like -S when parsing shebangs on Windows#28127
robobun wants to merge 4 commits intomainfrom
claude/fix-env-s-shebang-28126

Conversation

@robobun
Copy link
Copy Markdown
Collaborator

@robobun robobun commented Mar 15, 2026

Summary

  • Fix Windows shebang parser to skip env flags (e.g. -S, -u, -i, --split-string) when resolving the interpreter program name
  • Previously, #!/usr/bin/env -S node would cause the shim to look for -S as an executable, failing with "interpreter executable '-S' not found in %PATH%"
  • Affects any npm package using #!/usr/bin/env -S <interpreter> shebangs when installed on Windows (e.g. @google/gemini-cli)

Test plan

  • Added regression test test/regression/issue/28126.test.ts with 3 test cases:
    • env -S node shebang
    • env -S node --no-warnings (with additional args)
    • env -S bun shebang
  • All tests pass with bun bd test test/regression/issue/28126.test.ts

Closes #28126

🤖 Generated with Claude Code

When a bin script uses `#!/usr/bin/env -S node`, the Windows shebang
parser would incorrectly treat `-S` as the program name instead of
recognizing it as a flag to `env`. This caused packages like
`@google/gemini-cli` to fail with "interpreter executable '-S' not
found in %PATH%".

The fix skips any tokens starting with `-` after `/usr/bin/env` to
find the actual interpreter program name.

Closes #28126

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@robobun
Copy link
Copy Markdown
Collaborator Author

robobun commented Mar 15, 2026

Updated 11:34 PM PT - Mar 14th, 2026

@autofix-ci[bot], your commit 55ea5f0 has 6 failures in Build #39695 (All Failures):


🧪   To try this PR locally:

bunx bun-pr 28127

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

bun-28127 --bun

@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai Bot commented Mar 15, 2026

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 5b65963b-e581-4a39-90c1-0453575b3c4f

📥 Commits

Reviewing files that changed from the base of the PR and between 68dc997 and 55ea5f0.

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

Walkthrough

Env-based shebang parsing now skips env flags (including -S and others) to locate the real program, captures trailing arguments, and reconstructs the launcher for bun/node. Adds regression tests validating various env-flagged shebang scenarios (including -S, extra args, bun, and -u) to prevent Windows remapping errors.

Changes

Cohort / File(s) Summary
Shebang parsing change
src/install/windows-shim/BinLinkingShim.zig
Env-based shebang handling iterates past env flags (consuming flags like -u/--unset, -C/--chdir, -a/--argv0, and -S) to find the program token, records trailing arguments as rest_after_program, reconstructs the launcher (e.g., program rest_args...) for bun/node, and initializes Shebang with that launcher.
Regression tests for env-based shebangs
test/regression/issue/28126.test.ts
Adds four tests that create temporary packages with shebangs using env -S, extra args, bun as runtime, and a case with -u before the program; runs bun install and executes the linked bin, asserting no "interpreter executable ... not found" stderr and correct stdout/exit behavior (skips on musl where env -S is unsupported).
🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and concisely describes the main fix: skipping env flags when parsing shebangs on Windows.
Description check ✅ Passed The description provides a clear summary, explains the problem and solution, and includes a test plan with concrete examples.
Linked Issues check ✅ Passed The PR code changes address the root cause (Windows shebang parser treating -S as interpreter) and include regression tests to prevent recurrence.
Out of Scope Changes check ✅ Passed All changes are directly scoped to fixing the Windows shebang parser for env flags and adding relevant regression tests.

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

📝 Coding Plan
  • Generate coding plan for human review comments

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

Copy link
Copy Markdown
Contributor

@coderabbitai coderabbitai Bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🤖 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/BinLinkingShim.zig`:
- Around line 198-204: The loop that picks the shebang program (involving
tokenizer, program, token, and parseFromBinPath/bin_path) must also skip flag
operands for env flags that consume a following token; update the logic so when
a token matches flags that take values (e.g. -u, -C, -a or --argv0, -S or
--split-string and any equivalent long forms) you consume the next
tokenizer.next() result (handling orelse the same way as for token) and
continue, while still skipping standalone flags that don't take values; ensure
this adjusted behavior is applied inside the program: while loop before breaking
out to return the selected program.

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 0d1d7abd-a50c-478c-b8b8-ce3a32c5bc27

📥 Commits

Reviewing files that changed from the base of the PR and between 7a2ce3a and f6f97bd.

📒 Files selected for processing (2)
  • src/install/windows-shim/BinLinkingShim.zig
  • test/regression/issue/28126.test.ts

Comment on lines +198 to +204
// Skip flags passed to env (e.g. -S, -u, -i, --split-string)
// to find the actual program name.
const program = program: while (true) {
const token = tokenizer.next() orelse return parseFromBinPath(bin_path);
if (token.len > 0 and token[0] == '-') continue;
break :program token;
};
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.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🌐 Web query:

GNU coreutils envoption semantics: which options consume a following operand token (for example-u NAME, -C DIR, --unset NAME, --chdir DIR)?

💡 Result:

GNU coreutils env options that consume an argument (i.e., take/require an option-argument, which may be the following token) are:

  • -a arg, --argv0=arg (sets argv[0]) [1]
  • -u name, --unset=name (unsets a variable) [1][2]
  • -C dir, --chdir=dir (changes working directory) [1][2]
  • -S string, --split-string=string (splits string into arguments) [1][2]

Options that take an optional argument (shown as [=SIG]) are:

  • --block-signal[=SIG] [1][2]
  • --default-signal[=SIG] [1][2]
  • --ignore-signal[=SIG] [1][2]

For these --*-signal[=SIG] options, the argument is supplied as --opt=SIG; if you omit =SIG, env does not consume the next token as the argument. [2]

All other env options (e.g., -i, -0, -v, --list-signal-handling, --help, --version) do not consume a following operand token. [1][2]

Sources:
[1] GNU Coreutils manual, “env invocation” https://www.gnu.org/software/coreutils/manual/html_node/env-invocation.html
[2] Debian env(1) man page (coreutils) https://manpages.debian.org/bookworm/coreutils/env.1.en.html


Account for all env flags that consume a value token before selecting program.

Line 202 skips -u/-C themselves but not their required value tokens. Additionally, -a/--argv0 and -S/--split-string also consume following operands and are missing from the fix. Cases like #!/usr/bin/env -u NODE_OPTIONS node or #!/usr/bin/env -S "args" will resolve the wrong executable.

Proposed fix
-            const program = program: while (true) {
+            var skip_next_flag_value = false;
+            const program = program: while (true) {
                 const token = tokenizer.next() orelse return parseFromBinPath(bin_path);
-                if (token.len > 0 and token[0] == '-') continue;
+                if (skip_next_flag_value) {
+                    skip_next_flag_value = false;
+                    continue;
+                }
+                if (token.len > 0 and token[0] == '-') {
+                    if (eqlComptime(token, "-u") or eqlComptime(token, "-C") or
+                        eqlComptime(token, "-a") or eqlComptime(token, "-S") or
+                        eqlComptime(token, "--unset") or eqlComptime(token, "--chdir") or
+                        eqlComptime(token, "--argv0") or eqlComptime(token, "--split-string"))
+                    {
+                        skip_next_flag_value = true;
+                    }
+                    continue;
+                }
                 break :program token;
             };
🤖 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 198 - 204, The loop
that picks the shebang program (involving tokenizer, program, token, and
parseFromBinPath/bin_path) must also skip flag operands for env flags that
consume a following token; update the logic so when a token matches flags that
take values (e.g. -u, -C, -a or --argv0, -S or --split-string and any equivalent
long forms) you consume the next tokenizer.next() result (handling orelse the
same way as for token) and continue, while still skipping standalone flags that
don't take values; ensure this adjusted behavior is applied inside the program:
while loop before breaking out to return the selected program.

@github-actions
Copy link
Copy Markdown
Contributor

Found 5 issues this PR may fix:

  1. Bun crashes when trying to install gemini-cli (windows bin linking assertion fail) #23414 - Bun crashes when trying to install gemini-cli (windows bin linking assertion fail) - Windows-specific gemini-cli crashes likely caused by env flag parsing bug
  2. Globally installed CLIs are not working on my system #27924 - Globally installed CLIs are not working on my system - General global CLI failures that could result from shebang parsing issues
  3. Bun script with shebang clears cmd prompt in windows #18721 - Bun script with shebang clears cmd prompt in windows - Windows shebang handling problems potentially caused by incorrect env flag parsing
  4. Windows: Segmentation fault on Bun v1.3.0 when invoking global shim (claude, @anthropic-ai/claude-code) #23043 - Windows: Segmentation fault on Bun v1.3.0 when invoking global shim - Windows shim crashes that could result from executing invalid commands due to parsing bug
  5. Gemini CLI crash - ERROR ioctl(2) failed, EBADF #27285 - Gemini CLI crash - ERROR ioctl(2) failed, EBADF - Gemini-CLI specific crash potentially caused by failed process spawning from shebang parsing errors

If this is helpful, consider adding Fixes #<number> to the PR description to auto-close the issue on merge.

🤖 Generated with Claude Code

Handle env flags like `-u NAME`, `-C DIR`, `-a ARG` that consume the
next token as their value. Without this, a shebang like
`#!/usr/bin/env -S -u HOME node` would incorrectly identify "HOME"
as the program name.

Note: `-S`/`--split-string` is intentionally NOT treated as consuming
the next token because in shebang context the text after `-S` IS the
command to run.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@robobun
Copy link
Copy Markdown
Collaborator Author

robobun commented Mar 15, 2026

Addressed the review feedback — the parser now also skips value-consuming env flags (-u/--unset, -C/--chdir, -a/--argv0) by consuming their following token before continuing the search for the program name.

Important note on -S/--split-string: This flag is intentionally not treated as consuming the next token. In shebang context, #!/usr/bin/env -S node --no-warnings means -S tells env to split the rest of the line (node --no-warnings) into arguments — node IS the program, not an argument to -S. Consuming it would break the primary use case this PR fixes.

Added a test for #!/usr/bin/env -S -u HOME node to verify the value-consuming flag handling works correctly.

Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

LGTM — straightforward fix for Windows shebang parsing to skip env flags before the interpreter name, with good regression tests.

Extended reasoning...

Overview

This PR fixes the Windows shebang parser in BinLinkingShim.zig so that #!/usr/bin/env -S node correctly identifies node as the program instead of -S. The change replaces a single tokenizer.next() call with a loop that skips tokens starting with -, then reconstructs the launcher string via pointer arithmetic on slices from the same underlying buffer. A new test file with 3 regression cases covers the fix.

Security risks

None. This is Windows-specific shim code for bin linking during package installation. The pointer arithmetic is safe because both program and rest_after_program are slices into the same line buffer from the tokenizer, so the contiguous span computation is well-defined.

Level of scrutiny

Low-to-medium. This is a small, focused bug fix in a platform-specific shim. The logic is straightforward (skip dash-prefixed tokens in a loop), and the pointer arithmetic for reconstructing the launcher string is correct — rest() returns from the delimiter after the last token to the end of the buffer, so the computed slice spans "program rest_args...".

Other factors

  • No CODEOWNERS claim on these specific files.
  • The nit about -u HOME style flags with separate value arguments is a valid but extremely rare edge case; the PR is a clear improvement over the old code which broke on the very common -S flag.
  • Three regression tests cover the main scenarios (-S node, -S node --no-warnings, -S bun).
  • The PR fixes a real-world issue affecting packages like @google/gemini-cli.

Comment thread src/install/windows-shim/BinLinkingShim.zig
Claude Bot and others added 2 commits March 15, 2026 05:40
BusyBox env (used on Alpine/musl) does not support the -S flag,
causing these tests to fail at runtime. Wrap all tests in a
describe.skipIf(isMusl) block.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

LGTM — my previous nit about value-consuming flags has been addressed.

Extended reasoning...

Overview

This PR fixes the Windows shebang parser in BinLinkingShim.zig to skip env flags (like -S, -u, -C, -a) when resolving the interpreter program name. Previously #!/usr/bin/env -S node would cause the shim to treat -S as the executable. A new test file with 4 test cases covers the common scenarios.

Security risks

No security risks. The change only affects how shebang lines are parsed to find the interpreter name during bun install bin linking on Windows. No auth, crypto, or permissions code is involved.

Level of scrutiny

This is a focused bug fix in the package installer shim code. The pointer arithmetic for launcher reconstruction (line 222) is straightforward — both program and rest_after_program are slices from the same tokenizer operating on the same underlying line buffer, so the span calculation is safe. The logic for skipping value-consuming flags (-u, -C, -a) correctly consumes the next token. CodeRabbit earlier feedback about missing value-consuming flag handling was addressed in a follow-up commit.

Other factors

My previous review raised a nit about value-consuming flags like -u HOME — this has been fully addressed in the current code (lines 207-212) with a corresponding test case. The CI failures (musl builds, vendor warnings) are unrelated to this PR. The fix resolves a real-world issue affecting packages like @google/gemini-cli. No bugs were found by the automated bug hunting system.

Copy link
Copy Markdown
Contributor

@claude claude Bot left a comment

Choose a reason for hiding this comment

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

LGTM — focused bugfix with correct flag-skipping logic and good test coverage.

Extended reasoning...

Overview

This PR fixes the Windows shebang parser in BinLinkingShim.zig to skip env flags (like -S, -u, -C, -a) when resolving the interpreter program name. Previously, #!/usr/bin/env -S node would cause the shim to look for -S as the executable. A new test file with 4 test cases covers the main scenarios.

Security risks

No security risks. This is a Windows bin-linking shim parser fix — it only affects how shebangs are parsed to determine which interpreter to invoke. No auth, crypto, or permissions code is touched.

Level of scrutiny

Low-to-medium scrutiny is appropriate. The change is small (24 lines of Zig logic), well-scoped to a single parsing function, and includes regression tests. The pointer arithmetic for launcher reconstruction is safe — both program and rest_after_program are slices into the same line buffer from the tokenizer, so the computed offset is always valid. No CODEOWNERS are affected.

Other factors

My previous inline nit about value-consuming flags (e.g., -u HOME) has been fully addressed in the current code — lines 205-212 properly consume the next token for flags that take a separate value argument. The CI failures shown in the timeline are pre-existing vendor code warnings (libuv, tinycc) and build system issues unrelated to this PR. The fix addresses a real-world issue affecting popular packages like google/gemini-cli.

@ofek
Copy link
Copy Markdown

ofek commented Mar 18, 2026

Are there any further blockers for incorporating such a fix? Bun is unusable on Windows because of this.

@robobun
Copy link
Copy Markdown
Collaborator Author

robobun commented Mar 22, 2026

Closing as duplicate of #27925 — identical Zig code changes for the shebang -S flag parsing fix.

@robobun robobun closed this Mar 22, 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.

After installing Gemini using bun install -g @google/gemini-cli@nightly, running Gemini resulted in an error.

2 participants