‘NODE_ENV’ is not recognized as an internal or external command
Verified on Windows 11, Node 24.20.0 · OpusMill
operable program or batch file.
Install cross-env and put it in front of the variable:
npm install --save-dev cross-env
- "build": "NODE_ENV=production webpack" + "build": "cross-env NODE_ENV=production webpack"
That is the whole thing. It works unchanged on Windows, macOS and Linux, so you are not adding a Windows special case.
Why it happens
VAR=value command is shell syntax, not a
program. In bash it means “run command with
VAR set for this one invocation.” cmd.exe has no such
syntax at all, so it does the only thing it can: it treats
NODE_ENV=production as the name of the program you
asked it to run, fails to find it, and says so.
You hit it through npm because npm runs scripts through cmd.exe
on Windows and through /bin/sh everywhere else.
The same package.json therefore behaves differently
depending on who is typing npm run build. Nothing in your
JavaScript is wrong; the script never got as far as starting Node.
Reproducing it
Run this from cmd on any Windows machine:
C:\> NODE_ENV=production node -e "console.log(1)" 'NODE_ENV' is not recognized as an internal or external command, operable program or batch file.
Why not just set the variable first?
You can, and for a one-off in your own terminal it is fine
(set NODE_ENV=production in cmd,
$env:NODE_ENV="production" in PowerShell). But it is the
wrong fix for a package.json, for two reasons: it leaks
into every later command in that shell, and it means your build
instructions now differ per platform — which is the problem you
started with.
cross-env exists for exactly this and is downloaded about
25 million times a week. It is not a workaround for something rare.
The variants you may see instead
| Script | Same cause? |
|---|---|
NODE_OPTIONS=--experimental-vm-modules jest | Yes. Same fix. |
CI=true npm test | Yes. Same fix. |
TZ=UTC jest | Yes. Same fix. |
PORT=3000 node server.js | Yes. Same fix. |
Any NAME=value in front of a command in an npm script is
the same bug. If a script has several, cross-env takes
them all: cross-env A=1 B=2 node app.js.
If you maintain the package
This one almost never breaks for the people who install your
package — npm only runs your own scripts on their machine at
install time. It breaks for someone who cloned your repository and
typed npm test, and they usually give up rather than open
an issue, because they are not a contributor yet. That is why it
survives so long in projects that are otherwise well maintained.
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:
- The fix for 'rm' is not recognized
- The fix for spawn EINVAL on Windows
- The fix for ENOENT on node_modules/.bin
- Path too long on Windows
- EPERM on Windows
- Cannot find module, but only in CI
Background: I scanned the 600 most-downloaded
npm CLI packages — 17.4% have a package.json
script that cannot run on Windows.