Skip to content

fix: execute pnpm with shell on Windows - #18

Closed
NediarSK8 wants to merge 1 commit into
cloudflare:mainfrom
NediarSK8:fix-windows-pnpm-execution
Closed

fix: execute pnpm with shell on Windows#18
NediarSK8 wants to merge 1 commit into
cloudflare:mainfrom
NediarSK8:fix-windows-pnpm-execution

Conversation

@NediarSK8

@NediarSK8 NediarSK8 commented Aug 5, 2026

Copy link
Copy Markdown

Fixes ENOENT and EINVAL errors when running pnpm via child_process execFileSync on Windows by ensuring it runs with shell: true.

This fixes an issue where running pnpm run-local fails immediately on Windows with ENOENT or EINVAL errors coming from child_process.execFileSync.

Why it was broken
On Windows, pnpm is installed as a batch script (pnpm.cmd). Node.js's execFileSync cannot execute .cmd files natively without invoking a shell (like cmd.exe).

The fix
I updated the 5 instances of execFileSync("pnpm", ...) across the codebase to include { shell: process.platform === "win32" }.

Fixes ENOENT and EINVAL errors when running pnpm via child_process execFileSync on Windows by ensuring it runs with shell: true.

Co-authored-by: Gemini <gemini@google.com>
@github-actions

github-actions Bot commented Aug 5, 2026

Copy link
Copy Markdown


Thank you for your submission, we really appreciate it. Like many open-source projects, we ask that you sign our Contributor License Agreement before we can accept your contribution. You can sign the CLA by just posting a Pull Request Comment same as the below format.


I have read the CLA Document and I hereby sign the CLA


You can retrigger this bot by commenting recheck in this Pull Request. Posted by the CLA Assistant Lite bot.

@NediarSK8

Copy link
Copy Markdown
Author

I have read the CLA Document and I hereby sign the CLA

@snowyukitty

Copy link
Copy Markdown

I hit the same bug independently and filed #19 before spotting this PR — sorry for the duplicate
report, this got here first. One finding from that investigation that I think matters for this
change:

shell: true makes Node build a command line by joining the file and arguments with spaces,
without quoting them, so any argument containing a space is re-split by the shell:

$ node -e "..." "C:\...\space test dir\hello.txt"

no shell      -> ["C:\\...\\space test dir\\hello.txt"]
shell: true   -> ["C:\\...\\space", "test", "dir\\hello.txt"]

That is reachable from the run-dev-server.js hunk here. Its args are absolute paths built from
gk.dir, which comes from ROOT:

https://github.com/cloudflare/cloudflare-os/blob/aedcda8/run-dev-server.js#L297-L303

So on a checkout under, say, C:\Users\Jane Smith\src\cloudflare-os, wrangler would receive
-c C:\Users\Jane and two stray arguments. scripts/release/build-release.mjs has the same
exposure through --outdir <tmpdir>.

An alternative that avoids the quoting question entirely: npm_execpath holds pnpm's own JS entry
point, which node can run directly, so no shell is involved and arguments keep their exact values.

export function pnpmCommand(args) {
  const execPath = process.env.npm_execpath ?? "";
  return process.platform === "win32" && /[\\/]pnpm\.[cm]?js$/i.test(execPath)
      ? [process.execPath, [execPath, ...args]]
      : ["pnpm", args];
}

used as const [file, argv] = pnpmCommand([...]); execFileSync(file, argv, opts);.

The regex test is worth keeping: under npm run that same variable points at npm
(...\npm\bin\npm-cli.js), and substituting it unchecked would quietly run npm install against a
pnpm workspace. Falling through to plain "pnpm" keeps the failure loud instead.

I verified the end-to-end result on Windows 11 / Node 22 — pnpm run-local gets from the original
spawnSync pnpm ENOENT all the way to [wrangler:info] Ready on http://127.0.0.1:8787. Also worth
noting execFileSync("pnpm.cmd", ...) is not an option on current Node: it returns EINVAL since
the CVE-2024-27980 fix.

Happy for this PR to be the one that lands — just flagging the argument-splitting so it does not
trade one Windows failure for a subtler one. If it is useful I can push my version somewhere for
comparison, or leave a suggestion on the diff.

@NediarSK8

Copy link
Copy Markdown
Author

Happy for this PR to be the one that lands — just flagging the argument-splitting so it does not
trade one Windows failure for a subtler one. If it is useful I can push my version somewhere for
comparison, or leave a suggestion on the diff.

Thanks for the heads up. To be honest, this is my first public PR, and you spotted an flaw I'm not sure how to handle properly right now.

Feel free to push your fix directly to this PR branch or suggest on the diff if you'd like. That would help me learn and get this working for everyone.

@kentonv

kentonv commented Aug 7, 2026

Copy link
Copy Markdown
Member

Hmm, I was considering merging this PR as falling under the "small, trivially-verified" rule from our contributing policy. However, from the discussion, it sounds like it's actually more subtle. Nothing is trivial it seems. :(

So this also looks like something we're going to want to throw our own agents at to try to solve. The issue report #19 looks like it covers the details, so let's move the discussion there?

@kentonv kentonv closed this Aug 7, 2026
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.

4 participants