OpusMill

'C:\Program' is not recognized as an internal or external command

Verified on Windows 11, Node 24.20.0 · OpusMill

'C:\Program' is not recognized as an internal or external command,
operable program or batch file.
The fix

Nothing is wrong with your installation and no file is missing. Something ran a program whose path contains a space — almost always C:\Program Files\... — through cmd.exe without quoting it. cmd splits the line at the first space, so the command name became C:\Program and the rest became arguments.

In a batch file, a shortcut or a PATH entry: wrap the path in double quotes.

In Node: remove shell: true and spawn the executable directly. Do not just add quotes — that fixes the error you can see and leaves the one you cannot.

What is actually happening

cmd.exe has no concept of an escaped space. It takes everything up to the first space as the name of the thing to run. A path like C:\Program Files\nodejs\node.exe is therefore read as the command C:\Program, with Files\nodejs\node.exe as its first argument.

Here is that happening in a real .bat file on Windows 11. The executable exists at the full path in both runs; the only difference is the quoting:

--- unquoted ---
C:\...\Program Files Probe\nodejs\node.exe --version
'C:\...\Program' is not recognized as an internal or external command,
operable program or batch file.
  errorlevel=9009

--- quoted ---
"C:\...\Program Files Probe\nodejs\node.exe" --version
v24.20.0
  errorlevel=0

9009 is cmd.exe's code for “I could not find that command”. If you arrived here searching for that number, this is what it means: the command name cmd tried to resolve was not the one you thought you wrote.

In Node, the cause is nearly always shell: true

Without shell: true, Node hands the executable path to Windows directly and spaces are not a problem — there is no shell to split anything. Adding shell: true turns your carefully built path back into a string for cmd.exe to re-parse:

const exe = path.join(process.env.ProgramFiles, "nodejs", "node.exe");

spawnSync(exe, ["--version"]);                   // works
spawnSync(exe, ["--version"], { shell: true });  // 'C:\Program' is not recognized

The usual way this gets into a codebase is that shell: true was added to solve a different Windows problem — running a .cmd file, which throws EINVAL without it. The fix for that bug is the cause of this one.

Note what the failure looks like from your code's point of view:

const r = spawnSync(exe, ["--version"], { shell: true });

r.status  1
r.error   undefined   <-- there is no error object

This is a non-zero exit, not a thrown error. An if (err) return check does not see it, and neither does a try/catch. If you are not inspecting the exit code, the call fails and your program carries on as though it had worked.

The quiet version of this bug, which is worse

The obvious fix is to quote the path yourself. That does work for the path. It does not work for the arguments, because with shell: true Node concatenates the arguments onto the command string without escaping them — so cmd.exe splits those on spaces too.

The result is a call that succeeds while doing the wrong thing. All four runs below are on Windows 11, Node 24.20.0:

CallExitWhat the program received
quoted path, argument alpha 0 alpha — correct
quoted path, argument two words 0 two — silently truncated at the space
quoted path, argument quoted as well 0 two words — correct
no shell: true, nothing quoted 0 two words — correct

Row two is the one to worry about. Exit code 0. No error. No warning. A filename with a space in it, a commit message, a search term, a person's name — each arrives at the child process cut off at the first space, while every check you have says the call succeeded. The loud version of this bug at least tells you something is wrong.

Row four is why the real fix is to remove shell: true rather than to add quotes. With no shell involved there is nothing to re-parse, so neither the path nor the arguments need quoting at all.

Node itself now warns about this

Running any of the shell: true variants above on Node 24 prints:

(node:26032) [DEP0190] DeprecationWarning: Passing args to a child
process with shell option true can lead to security vulnerabilities,
as the arguments are not escaped, only concatenated.

“Not escaped, only concatenated” is the same mechanism described from the security side. If an argument can contain & or |, cmd.exe reads it as a command separator, and the truncation problem becomes a command injection problem. It is one line of code either way.

Which fix to use

SituationDo this
A .bat file, a shortcut, a PATH entry, a scheduled task Quote the path: "C:\Program Files\...". Nothing is concatenating arguments for you here, so quoting is the whole fix.
Node, spawning a real .exe Drop shell: true and pass the path and the arguments exactly as they are.
Node, and shell: true was there to run a .cmd Resolve the real executable and spawn that; or keep shell: true and quote both the path and every argument yourself. See the EINVAL page.
An environment variable holding a path Quote it at the point of use. %ProgramFiles% expands to a path containing a space on a default Windows install.

One thing not to do: move the program somewhere without a space in the path. That makes the symptom go away on your machine and leaves the bug in place for everyone else, which is how this survives in shipped software for years.

Check your own project for the rest of this class of bug. Paste your code or your package.json into the browser checker — nothing is uploaded, it runs on your machine — or run npx github:Hackierz/winbreak over the whole repository. --fix repairs the npm scripts that have one obvious answer and refuses the ones that need a human.

Other errors in the same family:

Background: I scanned the 600 most-downloaded npm CLI packages — 17.4% have a package.json script that cannot run on Windows.