npm ENOENT package.json Not Found in CI - Fix Missing Manifest
ENOENT for package.json means npm looked for the manifest in the current directory and there was no file there. It is almost always a working-directory or checkout problem.
What this error means
An install or run step fails right away with code ENOENT and a path ending in package.json, saying npm could not read the file. Running locally from the project root works fine.
npm ERR! code ENOENT
npm ERR! syscall open
npm ERR! path /home/runner/work/repo/package.json
npm ERR! errno -2
npm ERR! enoent ENOENT: no such file or directory, open '.../package.json'Diagnose it: reproduce the CI install locally
Install failures are usually environment drift rather than a broken lockfile: a different package-manager major, a different Node version, or a cache that is being restored from a run with different inputs. Reproduce the CI conditions before changing the lockfile, because regenerating it hides the real cause.
# match the runner exactly, then install from a clean slate
node --version && npm --version
rm -rf node_modules
npm ci --foreground-scripts
# if that succeeds locally but fails in CI, the difference is the cache
# or the package-manager version, not your lockfileCommon causes
The job runs npm from the wrong directory
The package.json lives in a subfolder, but the step runs at the repo root (or vice versa), so npm finds no manifest.
The checkout did not include the manifest
A missing or shallow checkout, or a sparse-checkout that excluded the project folder, leaves no package.json on disk.
How to fix it
Run npm in the directory that contains package.json
- Confirm where package.json lives in the repo.
- Set the working directory for the install step to that folder.
- name: Install
working-directory: ./app
run: npm ciEnsure the repo is checked out first
- Add the checkout step before any npm step.
- Verify the path with ls so the manifest is present.
- uses: actions/checkout@v4
- run: ls package.json && npm ciVerify the fix survives a cold cache
A green run immediately after a fix often proves nothing, because it restored a cache written before the change. Force a cold install once to confirm the fix is real.
# temporarily bust the cache key to prove the fix on a cold runner
- uses: actions/setup-node@v4
with:
node-version: 22
cache: npm
cache-dependency-path: package-lock.json
# then bump this suffix once, run, and remove it
# key: ${{ runner.os }}-node-${{ hashFiles('package-lock.json') }}-v2How to prevent it
- Always run actions/checkout before npm, set working-directory explicitly for subfolder projects, and add a quick ls of package.json to fail fast with a clear message.