Skip to content
Latchkey

Node.js "cross-env: command not found" in CI

A script uses cross-env to set environment variables portably, but the binary is missing in CI. With dev deps pruned (or never installed), the shell cannot find cross-env and exits 127.

What this error means

A script that starts with cross-env NODE_ENV=production ... fails with cross-env: command not found and exit code 127. The same script works locally where dev deps are present.

node
> build
> cross-env NODE_ENV=production vite build

sh: cross-env: command not found
npm ERR! code 127

Diagnose it: the shell in CI is not your shell

Package scripts run under a different shell, a different PATH, and a non-interactive environment on a runner. Most scripts that fail only in CI are relying on something the login shell gave them locally: a tool on PATH, an environment variable from a dotfile, or a TTY.

Terminal
# what the script can actually see
npm run env | grep -E "^(PATH|NODE_ENV|CI)=" 

# is the binary on PATH for the script, not just for you?
npm exec -- which <tool> || echo "not resolvable from npm scripts"

# run the exact script with tracing
sh -x -c "$(node -p "require('./package.json').scripts.build")"

Common causes

devDependencies pruned in CI

cross-env is a devDependency. A --omit=dev install removes it, so the build script cannot find the binary.

cross-env not declared at all

The script relies on cross-env but it was never added to package.json, so it is absent on a clean install.

How to fix it

Install cross-env and keep dev deps for builds

Add cross-env as a devDependency and run a full install in build jobs.

Terminal
npm install -D cross-env
# build job uses a full install
npm ci

Set env without cross-env on POSIX runners

On Linux CI you can set env inline and drop the dependency entirely if Windows support is not needed.

workflow
NODE_ENV=production vite build

Make failures fail the job

A multi-command script can report success while a middle command failed, which produces the worst kind of CI result: a green build that shipped something broken.

.github/workflows/ci.yml
# pipefail is NOT set by default in every runner shell
- name: Build
  shell: bash
  run: |
    set -euo pipefail
    npm run build | tee build.log

How to prevent it

  • Keep devDependencies installed for build jobs.
  • Declare every CLI a script uses in package.json.
  • Drop cross-env where only POSIX runners run the script.

Frequently asked questions

What causes Node.js "cross-env: command not found" in CI?
There are 2 common causes: devdependencies pruned in ci and cross-env not declared at all. cross-env is a devDependency.
How do I fix Node.js "cross-env: command not found" in CI?
There are 2 fixes depending on which cause you have: install cross-env and keep dev deps for builds and set env without cross-env on posix runners. Work through them in order, since the first is the most common.
What does Node.js "cross-env: command not found" in CI actually mean?
A script that starts with cross-env NODE_ENV=production ...
How do I stop Node.js "cross-env: command not found" in CI happening again?
Keep devDependencies installed for build jobs. The prevention section lists 3 changes that keep it from recurring.

Related guides

References

Not every red build is your code. Latchkey repairs the ones that are not, on the runner. Start free → 30-day trial · No credit card