You review the Codex PR. package.json now lists zod as a new dependency, and the code imports from zod. Everything looks fine until CI runs npm ci and fails:
npm error `npm ci` can only install packages when your package.json and
package-lock.json or npm-shrinkwrap.json are in sync. Please update your
lock file with `npm install` before continuing.
npm error Missing: zod@3.23.8 from lock file
Codex added the dependency to package.json but never ran npm install, so package-lock.json does not contain zod. Worse variants: the agent ran npm install --no-save, edited the lockfile by hand and wrote a fake integrity hash, or used the wrong package manager and dropped a second lockfile next to your real one.
Fastest fix: on your own machine, check out the branch, run the project’s install command (npm install, pnpm install, or yarn install), and commit the regenerated lockfile alongside package.json. That clears the immediate red CI. The rest of this page stops it from happening on every future Codex run by adding an AGENTS.md rule, a setup-script step, and a required CI sync check.
This is a small surface area but it breaks npm ci, breaks reproducible builds, and risks Codex resolving a different version on its next run than your teammates have locally.
Which bucket are you in?
| Symptom in the diff / CI log | Most likely cause | Jump to |
|---|---|---|
package.json changed, lockfile did not; Missing: <pkg> from lock file | Agent skipped install | Cause 1 |
Transcript shows npm install <pkg> --no-save | Install ran but lockfile not written | Cause 2 |
New package-lock.json next to existing pnpm-lock.yaml | Wrong package manager | Cause 3 |
Large lockfile diff, npm ci errors EINTEGRITY / integrity checksum failed | Hand-edited lockfile | Cause 4 |
| Install succeeded in the agent log but no lockfile change in the PR | setup.sh ran install, changes not committed | Cause 5 |
npm ci fails on a branch nobody touched (does not satisfy) | Upstream patch published; not Codex’s fault | Cause 6 |
Common causes
1. Agent skipped the install step
The Codex sandbox added "zod": "^3.23.0" to package.json and moved on. It never executed npm install, so the lockfile is untouched.
How to spot it: git diff shows package.json changed but package-lock.json / pnpm-lock.yaml / yarn.lock did not. npm ci fails with Missing: <pkg> from lock file.
2. Agent ran npm install --no-save
The model thought it was being cautious: install for testing, do not modify files. The package lands in node_modules for the agent’s tests but never in the lockfile.
How to spot it: the transcript shows npm install <pkg> --no-save or npm install --no-save. The lockfile is unchanged.
3. Agent ran the wrong package manager
Your repo uses pnpm but Codex ran npm install, which generates package-lock.json while you ship pnpm-lock.yaml. Now you have two lockfiles and no clear source of truth.
How to spot it: a new package-lock.json appears next to the existing pnpm-lock.yaml. Whichever lockfile your CI enforces, the other tool’s install fails.
4. Agent hand-edited the lockfile
The model saw a sync error in a previous run and decided to fix it by editing package-lock.json directly. The integrity shasums are now fabricated, so npm ci fails its integrity check.
How to spot it: the package-lock.json diff is large and looks plausible, but npm ci errors with npm error code EINTEGRITY or integrity checksum failed.
5. setup.sh runs install but the changes are not committed
Codex’s setup script runs npm install to provision the container, but those lockfile changes never make it into the commit. Per OpenAI’s Cloud environments docs (as of June 2026), the setup script runs in a separate Bash session before the agent; its installed dependencies and file changes persist into the cached workspace, but Codex never auto-commits them. If the agent then edits package.json without re-running install, only its own edits get committed. The cached container can also resume on an older commit, in which case Codex runs the optional “maintenance script” to refresh dependencies — that, too, does not produce a commit.
How to spot it: the lockfile diff exists in the agent sandbox but not in the PR. The agent transcript shows install succeeded.
6. Upstream published a patch — not Codex’s fault
A real edge case worth ruling out: under npm 11 (current as of June 2026), npm ci can fail on a lockfile nobody touched. When a new patch of a transitive dependency is published to the registry, npm ci re-resolves the range from package.json and the freshly resolved version no longer matches what the lockfile pinned, producing an error like lock file's @types/node@24.13.1 does not satisfy @types/node@24.13.2. See npm/cli issue #8693 and #8726.
How to spot it: the failure mentions does not satisfy rather than Missing, and it reproduces on main with no Codex changes. The fix is to run npm install once to refresh the lockfile and commit it — pin tighter ranges if it keeps recurring.
Shortest path to fix
Step 1: An AGENTS.md “always install after package.json change” rule
AGENTS.md (in the repo root) is the file Codex reads for project-specific commands and conventions. Add an explicit dependency rule:
## Dependency rules
After any change to `package.json` (add, remove, version bump):
1. Run the install command for the active package manager:
- `npm install` if `package-lock.json` exists
- `pnpm install` if `pnpm-lock.yaml` exists
- `yarn install` if `yarn.lock` exists
2. Stage both `package.json` and the lockfile in the same commit.
3. Do not run `npm install --no-save`.
4. Do not hand-edit any lockfile. If the lockfile seems wrong, delete it
and re-run install so the manager regenerates it.
5. Do not introduce a second lockfile. Use only the one already in the repo.
In the PR description, list new packages and the reason for each.
A short rule with the exact commands beats a paragraph of prose: agents follow imperative, numbered instructions far more reliably.
Step 2: Bake install into the setup script so changes are visible
Make sure your Codex setup script always runs install in a way that surfaces lockfile changes. Note from the docs: setup scripts run in their own Bash session, so anything you export there does not carry into the agent phase (persist real env vars via ~/.bashrc or the environment’s secret settings instead). Keep this script about provisioning, not state.
#!/usr/bin/env bash
set -euo pipefail
# Detect package manager
if [ -f pnpm-lock.yaml ]; then
pnpm install --frozen-lockfile=false
elif [ -f yarn.lock ]; then
yarn install
else
npm install
fi
# Show the agent which files changed during setup so it knows to commit them
git status --porcelain
Then in AGENTS.md:
After setup completes, run `git status` and commit any lockfile changes
along with your task changes.
Step 3: A CI check that the lockfile matches package.json
This is the real guardrail. Make it a required status check and Codex (or any human) cannot land an out-of-sync lockfile.
For npm:
# .github/workflows/lockfile-check.yml
name: Lockfile check
on: [pull_request]
jobs:
check:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: actions/setup-node@v4
with: { node-version: '20' }
- name: Verify lockfile is in sync
run: npm ci
- name: Verify no extra lockfiles
run: |
count=$(ls -1 package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null | wc -l)
if [ "$count" -gt 1 ]; then
echo "More than one lockfile present"
ls -la package-lock.json pnpm-lock.yaml yarn.lock 2>/dev/null
exit 1
fi
npm ci exits non-zero if package.json and package-lock.json are out of sync.
For pnpm, the equivalent strict install is:
- name: Verify lockfile is in sync
run: pnpm install --frozen-lockfile
For yarn (Berry), use yarn install --immutable.
Step 4: A “show lockfile diff in PR” rule
AGENTS.md:
When a PR changes any lockfile, include this section in the PR body:
## Lockfile changes
Run `git diff --stat package-lock.json` (or pnpm-lock.yaml) and paste the
output. Then:
- List packages added
- List packages removed
- List packages whose major version changed
- For each, link the changelog / release notes
This forces the agent to surface dependency churn for reviewers instead of burying a 4,000-line lockfile diff.
Step 5: Block hand-edited lockfiles
Add a pre-commit hook that flags suspicious lockfile changes (a lockfile change with no corresponding package.json change is the tell for “the agent edited it by hand to silence an error”):
mkdir -p .git/hooks
cat > .git/hooks/pre-commit <<'EOF'
#!/usr/bin/env bash
set -euo pipefail
if git diff --cached --name-only | grep -qE '(package-lock\.json|pnpm-lock\.yaml|yarn\.lock)$'; then
# If a lockfile changes, package.json must also change (unless it is a full re-gen)
if ! git diff --cached --name-only | grep -q 'package\.json$'; then
echo "WARNING: lockfile changed but package.json did not."
echo "If this is intentional (e.g. dedupe), confirm by setting LOCKFILE_REGEN=1."
[ "${LOCKFILE_REGEN:-0}" = "1" ] || exit 1
fi
fi
EOF
chmod +x .git/hooks/pre-commit
Local hooks under .git/hooks are not shared by clone, so for a team set this up through Husky or a committed core.hooksPath directory. The CI check in Step 3 is the backstop that always runs.
Step 6: Pin the package manager via the packageManager field
In package.json:
{
"packageManager": "pnpm@9.0.0"
}
Then enable Corepack in your setup script so the pinned manager is the one that runs:
corepack enable
# Optional: pre-download the pinned version so it is cached before the agent runs
corepack prepare pnpm@9.0.0 --activate
corepack enable installs shims that read the packageManager field, so pnpm resolves to exactly 9.0.0. Do not write a bare corepack prepare --activate with no version — it needs a descriptor like pnpm@9.0.0. With this in place Codex cannot accidentally invoke a different manager and generate a foreign lockfile.
How to confirm it’s fixed
After regenerating and committing the lockfile, verify locally before pushing:
rm -rf node_modules
npm ci # or: pnpm install --frozen-lockfile
git status --porcelain # must print nothing
A clean npm ci plus an empty git status means package.json and the lockfile agree and the install is reproducible. The CI check from Step 3 then enforces the same on every future PR.
Prevention
AGENTS.mdmandates install after anypackage.jsonedit and names which manager to use.- The Codex setup script runs install and surfaces lockfile changes via
git status. - CI runs
npm ci/pnpm install --frozen-lockfile/yarn install --immutableas a required check. - A pre-commit hook (and CI) flags lockfile changes without a corresponding
package.jsonchange. - Pin
packageManagerinpackage.jsonand enable Corepack in the setup script. - The PR body requires a “Lockfile changes” section listing added / removed / bumped packages.
FAQ
Why does npm ci fail when npm install worked locally for the agent?
npm install mutates the lockfile to match package.json; npm ci does the opposite — it refuses to install unless they already agree, then installs strictly from the lockfile. The agent’s local npm install succeeded precisely because it would have written the lockfile, but if that write never reached the commit, CI’s npm ci sees a mismatch.
My lockfile is correct but npm ci still fails with “does not satisfy” — what now?
That is bucket 6, not a Codex bug. Under npm 11 a newly published upstream patch can make npm ci re-resolve to a version the lockfile does not pin. Run npm install once to refresh the lockfile, commit it, and consider tightening the range. Track it via npm/cli issue #8693.
Can I just have CI auto-run npm install and commit the lockfile for me?
You can, but it hides the dependency change from review and lets the agent’s choices land unaudited. Prefer failing the PR and forcing the agent (or you) to commit a deliberate lockfile, with the “Lockfile changes” section in the PR body.
Should I delete the wrong lockfile or the right one when both appear?
Delete the one the repo did not originally ship. If you use pnpm, remove the stray package-lock.json, then run pnpm install to confirm pnpm-lock.yaml is still in sync. Pinning packageManager (Step 6) prevents the duplicate from coming back.
Does Codex commit changes my setup script makes?
No. As of June 2026, setup scripts run in a separate Bash session before the agent phase; their installed dependencies and file changes persist into the cached workspace, but Codex only commits the agent’s own edits. If install runs only in setup, re-run it in the agent phase (per the AGENTS.md rule) so the lockfile change is part of the commit.