vercel / vercel/vercel-plugin

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

Open
#175 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
287
Forks
58
Avg merge
1d 1h
Merged PRs (30d)
17

Description

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.

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in hooks/src/session-start-profiler.mts at checkVercelCli and compare it with the compiled hooks/session-start-profiler.mjs that runs. Run the Windows minimal reproduction and inspect the nearby npm view call for the same spawning pattern. Done means Windows npm-installed .cmd and .bat CLIs are detected and the session-start hook no longer reports an installed CLI as missing.

Written by the indexing model from the issue text.

Assessment

Tech stack
node.js, typescript
Domain
tooling
Issue type
Bug
Difficulty
3/5
Estimated time
1-2 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
76/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.