Skip to content

fix(compile): strip duplicate exe path from argv when standalone binary relaunches#33016

Merged
bartlomieju merged 2 commits intomainfrom
fix/compile-standalone-relaunch-argv
Mar 26, 2026
Merged

fix(compile): strip duplicate exe path from argv when standalone binary relaunches#33016
bartlomieju merged 2 commits intomainfrom
fix/compile-standalone-relaunch-argv

Conversation

@bartlomieju
Copy link
Copy Markdown
Member

Summary

Fixes an issue where compiled npm CLI tools that relaunch themselves
(e.g. @google/gemini-cli) would have the binary path leak into
process.argv as a user argument.

When Node.js apps relaunch themselves they typically do:

spawn(process.execPath, [process.argv[1], ...userArgs])

In standalone binaries process.argv[1] is the exe path (set by #32990),
so the child process receives the exe path as its first CLI argument.
The standalone arg parser only skipped argv[0], so the duplicate exe
path ended up in Deno.args / process.argv, causing yargs-based CLIs
to misinterpret it as a positional argument.

For example, running compiled gemini would show the binary path
pre-filled as the initial prompt, because yargs parsed
/Users/x/.deno/bin/gemini as a positional arg.

Fix

In cli/rt/binary.rs, detect when the first CLI arg (after skipping
argv[0]) matches the current executable path and strip it. This
handles the standard Node.js relaunch pattern without affecting normal
usage.

Test plan

  • Extended standalone_spawn_self spec test to verify user args
    pass through correctly and exe path doesn't leak
  • Existing standalone_process_argv test still passes
  • Manual verification with compiled @google/gemini-cli

🤖 Generated with Claude Code

…ry relaunches

When Node.js apps (e.g. @google/gemini-cli) relaunch themselves via
`spawn(process.execPath, [process.argv[1], ...args])`, the standalone
binary would include the exe path as a user argument in `Deno.args` /
`process.argv`. This is because `process.argv[1]` in standalone mode
is the exe path (set by #32990), and the binary's arg parser only
skipped `argv[0]`.

Now the standalone binary detects when the first CLI arg matches the
current exe path and strips it, preventing the path from leaking into
user arguments.

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

@bartlomieju bartlomieju left a comment

Choose a reason for hiding this comment

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

The approach is correct — this is essentially what Node.js SEA does to handle the relaunch pattern. Given that #32990 set process.argv[1] = execPath, this is the natural place to strip the duplicate.

Minor style suggestion: the first boolean flag can be simplified by just handling the first element separately:

let current_exe = std::env::current_exe().ok();
let mut args_iter = cli_args.into_iter();
args_iter.next(); // skip argv[0]

// Node.js apps relaunch with spawn(process.execPath, [process.argv[1], ...args]).
// In standalone mode process.argv[1] === execPath (#32990), so the first arg
// after argv[0] is a duplicate of the exe path. Strip it.
if let Some(first) = args_iter.next() {
    let is_exe_dup = current_exe
        .as_ref()
        .is_some_and(|exe| Path::new(&first) == exe.as_path());
    if !is_exe_dup {
        metadata.argv.push(first.into_string().unwrap());
    }
}
for arg in args_iter {
    metadata.argv.push(arg.into_string().unwrap());
}

One edge case worth noting in a comment: if a user intentionally passes the exe path as the first argument (./myapp ./myapp --foo), it would be silently stripped. In practice this doesn't happen with standalone binaries, but worth acknowledging the tradeoff.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
@bartlomieju bartlomieju merged commit 522e071 into main Mar 26, 2026
112 checks passed
@bartlomieju bartlomieju deleted the fix/compile-standalone-relaunch-argv branch March 26, 2026 21:29
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant