TypeScript

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.

Mahdi Ben RhoumaMahdi Ben Rhouma
7 min read

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:

text
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.log

The 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.

bash
npm install
npm start

This 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.

bash
npm install --include=dev

The 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.

bash
npm install typescript --save-dev

Check 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#

bash
ls node_modules/.bin
npm start

That 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

|

Have more questions? Contact us

Still stuck? Paste your error.

Paste the error message or stack trace — we check it against every fix and tool on the site.

Matching happens in your browser. When you search, a cleaned error signature is sent so we can spot errors we do not cover yet — never the raw text, your IP address or your e-mail. Do not paste passwords or keys. Details

Written by

Mahdi Ben Rhouma
Mahdi Ben Rhouma

Full-Stack Dev — Next.js & Supabase

Solo developer building SaaS products with Next.js and Supabase. Writing about production patterns the official docs skip.

Remote