Fix: Local package.json exists, but node_modules missing
Why cloning a repository never brings its dependencies with it, and the one command that turns a missing-binary failure into a working start script.
A clone that has a package.json but no node_modules folder is not broken: it is a clone, nothing more. Every npm start, npm run build or npm test that calls a binary installed by a dependency will fail until that dependency is actually installed, and npm says so plainly — if your npm version still prints the warning at all.
TL;DR#
node_modules is never checked into a repository, so a fresh clone has only package.json and the lockfile — no installed packages, no .bin binaries. Running a script before installing fails because the binary it calls (webpack-dev-server, tsc, vite…) does not exist yet. The fix is always the same: run npm install, then run the script again. If the missing package is a build tool that only ever ran because it was installed globally on one machine, add it as a devDependency so every clone gets it; and if the install itself ran with NODE_ENV=production, re-run it with --include=dev to actually bring devDependencies in.
The error#
This is the exact failure from the original Stack Overflow question (265,700 views), cloning a React/Redux project and running npm start:
npm start
> react-redux@1.0.0 start /home/workspace/assignment
> webpack-dev-server --config ./configs/webpack/webpack.config.development.js
sh: 1: webpack-dev-server: not found
npm ERR! file sh
npm ERR! code ELIFECYCLE
npm ERR! errno ENOENT
npm ERR! syscall spawn
npm ERR! react-redux@1.0.0 start: `webpack-dev-server --config ./configs/webpack/webpack.config.development.js`
npm ERR! spawn ENOENT
npm ERR!
npm ERR! Failed at the react-redux@1.0.0 start script.
npm ERR! This is probably not a problem with npm. There is likely additional logging output above.
npm WARN Local package.json exists, but node_modules missing, did you mean to install?
npm ERR! A complete log of this run can be found in:
npm ERR! /home/.npm/_logs/2018-05-15T16_32_44_571Z-debug.logThe shape repeats with any binary: tsc, vite, jest, eslint, a custom CLI from a private package — whichever script line the start (or build, test…) entry in package.json happens to run.
Why it happens#
npm install does two things that matter here: it resolves and downloads every package listed in dependencies and devDependencies into node_modules, and it links each package's executables into node_modules/.bin, which is what the shell actually finds on its PATH when a script runs a bare command name. The npm docs describe npm install as installing "a package and any packages that it depends on" (npm-install) — nothing in package.json runs that step for you.
node_modules is excluded from version control by almost every .gitignore template, because it is large, rebuildable and often platform-specific (native modules compile differently per OS/architecture). So a git clone — or copying a project to a new machine, or restoring a Docker image layer without a COPY node_modules step — always produces a tree with package.json present and node_modules absent. npm start does not install anything first; it just runs the start script's command line through the shell. If that command is a binary that only exists inside node_modules/.bin once installed, the shell reports it as "not found" (sh: 1: webpack-dev-server: not found, spawn ENOENT), and npm surfaces the lifecycle failure alongside its own hint.
A second, less obvious cause produces the identical symptom even after npm install has already run once: a build tool used by a script line was never declared as a dependency at all. The npm docs are specific about this for devDependencies — they "will be installed when doing npm link or npm install from the root of a package" (package.json#devdependencies) — which only holds if the tool is actually listed there. A project built on a machine with typescript installed globally runs tsc fine locally and fails identically on any other machine, with or without a prior npm install, until typescript is added to devDependencies.
Fix#
1. Install the project's dependencies, then rerun the script.
npm install
npm startThis is the accepted answer on the original question (267 votes): npm install reads dependencies and devDependencies from package.json, downloads everything listed, and populates node_modules/.bin with their executables. Only after this step does npm start have webpack-dev-server (or whatever the script calls) on its effective PATH.
2. If the install ran under NODE_ENV=production, it skipped devDependencies on purpose.
npm install --include=devThe current npm docs for npm-install state it directly: "With the --production flag (or when the NODE_ENV environment variable is set to production), npm will not install modules listed in devDependencies" (npm-install). A CI job, a Docker build stage or a hosting platform that sets NODE_ENV=production before npm install will leave build tools like webpack, tsc or vite out even though package.json lists them — the warning and the "not found" error look identical, but the fix here is --include=dev, not repeating a plain npm install.
3. If the binary was never listed as a dependency at all, add it.
npm install typescript --save-devCheck the script line that failed against dependencies/devDependencies in package.json. If the package genuinely is not there — common for a CLI someone only ever had installed globally — npm install <pkg> --save-dev both installs it locally and records it, so the next clone does not repeat the failure.
Verify the fix#
ls node_modules/.bin
npm startThat listing should now include the binary the failing script called (cowsay and cowsay.cmd in this reproduction's throwaway package.json, webpack-dev-server in the original question's); npm start (or the script that failed) should run it instead of reporting "not found". Reproduced locally on npm 11.19.0 (Node 26.7.0, 31 October 2026): a scratch package.json with a start script calling a binary from an uninstalled devDependency fails with the shell's "not found" before npm install, and runs cleanly immediately after it — one change, nothing else, between failure and success (full output in this draft's repro.log).
One email a month — the latest explained errors
What the site published that month — mostly Next.js, Supabase and Node errors, explained. You confirm by email; unsubscribe any time in one click.
When node_modules exists but the fix still looks the same#
Occasionally the failure happens even though node_modules is already there: a corrupted or partially written install (a killed process, a full disk, or a container that lost its volume mid-install) can leave the folder present but missing the one package a script needs. Re-running npm install is still the fix; if it repeatedly fails to finish, delete node_modules and package-lock.json and reinstall from scratch instead of layering another partial install on top of a damaged one.
If a clean install fails outright with a permission error rather than a missing binary, check the permission layer around package-lock.json first — a root-owned lockfile stops npm install from completing. If the install itself seems to hang rather than fail, see the buildDeps stall this site documents separately for telling a slow registry from a silent one. And if the script itself fails to even start the binary — not because it's missing, but because Windows refuses the spawn() call on a .cmd file without the shell option — see the spawn EINVAL case from npx install-peerdeps on Windows.
Treat this failure as a reminder to install before you run, not as a sign anything in the project is wrong: the repository never stored node_modules in the first place.
Frequently Asked Questions
Still stuck? Paste your error.
Paste the error message or stack trace — we check it against every fix and tool on the site.
Continue Reading
Fix npm WARN package.json: No repository field
The warning is informational and harmless. Silence it for good in an application, fill it in properly for a library, and know why modern npm no longer prints it.
Fix EACCES: permission denied, mkdir node_modules/.cache
Reproduced in Docker: why the cache directory cannot be created, the one-line Dockerfile move that fixes it, and why an old volume keeps the error alive after a rebuild.
Fix npm EACCES: permission denied on package-lock.json
Separate a local lockfile permission failure from cache and global-install errors, then fix the owner without changing the dependency tree.


