Skip to content

Session-start hook reports "The Vercel CLI is not installed" on Windows — execFileSync on vercel.cmd throws EINVAL #175

Description

@Lacave91

What happened

On Windows the session-start hook always injects this into the agent's context:

IMPORTANT: The Vercel CLI is not installed.
Strongly recommend the user install it with npm i -g vercel to unlock agentic features like vercel env pull, vercel deploy, and vercel logs.

The CLI is installed and authenticated:

$ vercel --version
Vercel CLI 56.5.0
$ vercel whoami
<my username>

The practical cost is not the wrong line, it's what the agent does with it: it takes the statement at face value and avoids vercel ls / vercel inspect for the rest of the session, so it can't check its own deployments.

Environment

  • Plugin: vercel 0.48.0 (installed from anthropics/claude-plugins-official)
  • OS: Windows 11
  • Node: v24.13.0
  • vercel installed globally with npm, so it resolves to %APPDATA%\npm\vercel.cmd

Cause

In hooks/src/session-start-profiler.mts (and identically in the compiled hooks/session-start-profiler.mjs that actually runs):

  1. checkVercelCli() (~line 416) resolves the binary correctly. resolveBinaryFromPath does append the Windows executable extensions and finds vercel.cmd, so this part is not the bug.
  2. It then calls execFileSync(vercelBinary, ["--version"]) at ~line 425 without shell: true.
  3. Since Node 18.20 / 20.12 (the CVE-2024-27980 hardening), spawning a .cmd or .bat without a shell throws EINVAL.
  4. The catch at ~line 433 swallows it and returns { installed: false } — which is what produces the message.

So the hook finds the CLI and then concludes it does not exist.

Minimal repro

node -e "const{execFileSync}=require('child_process');const p=require('path').join(process.env.APPDATA,'npm','vercel.cmd');try{execFileSync(p,['--version'],{stdio:['ignore','pipe','ignore']});console.log('ok')}catch(e){console.log(e.code)}"

Prints EINVAL. Adding shell: true (with the path quoted) makes it print ok.

Blast radius

  • The npm view call at ~line 449 has exactly the same shape and would fail the same way, but it is unreachable: the catch on the first call returns before it.
  • The "CLI is outdated (x → y)" branch lives in the else of installed, so on Windows it can never fire. Windows users don't get a false statement there — they get no upgrade notice at all.
  • Any npm-installed CLI on Windows resolves to a .cmd shim, so the pattern isn't specific to vercel.

Suggested fix

Spawn through a shell on Windows when the resolved path ends in .cmd / .bat, or resolve to the JS entry point behind the shim instead of the shim itself. A bare shell: true needs care with argument escaping, so quoting the resolved path explicitly is probably the safer form.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions