#16428·pnpm

`pnpm install --frozen-lockfile` still rejects a fresh lockfile when an injected package's peer is optional and satisfied by a sibling importer

Author: runspiredCreated Sep 30, 2026Updated Sep 30, 2026
Labelsarea: lockfilearea: monorepoarea: peersregressionarea: resolution

Last pnpm version that worked

12.6.x

pnpm version

12.7.0 12.8.0 and 12.8.1

Code to reproduce the issue

The script below fails on 12.7.0, 12.8.0, and 12.8.1. It is a variant of the repro in #16332: instead of a required peer dependency, lib's peer is declared optional via peerDependenciesMeta, and a different workspace project (app2) is the one that actually depends on both lib (injected) and peer.

Save this as repro.sh:

bash
#!/bin/sh
set -eu
PNPM_VERSION=${1:-12.8.1}
dir=$(mktemp -d) && cd "$dir"
mkdir app app2 lib peer
printf 'packages:\n  - app\n  - app2\n  - lib\n  - peer\n' > pnpm-workspace.yaml
echo "{\"name\":\"root\",\"private\":true,\"packageManager\":\"pnpm@$PNPM_VERSION\"}" > package.json
echo '{"name":"peer","version":"1.0.0"}' > peer/package.json
cat > lib/package.json <<'EOF'
{
  "name": "lib",
  "version": "1.0.0",
  "peerDependencies": { "peer": "^1.0.0" },
  "peerDependenciesMeta": { "peer": { "optional": true } }
}
EOF
echo '{"name":"app","private":true,"dependencies":{"lib":"workspace:*"},"dependenciesMeta":{"lib":{"injected":true}}}' > app/package.json
echo '{"name":"app2","private":true,"dependencies":{"lib":"workspace:*","peer":"workspace:*"},"dependenciesMeta":{"lib":{"injected":true}}}' > app2/package.json

pnpm --version
pnpm install --lockfile-only
pnpm install --no-frozen-lockfile   # succeeds, lockfile unchanged
rm -rf node_modules */node_modules
pnpm install --frozen-lockfile      # fails on 12.7.0, 12.8.0, and 12.8.1

### Expected behavior

`pnpm install --frozen-lockfile` should complete successfully, since the preceding --no-frozen-lockfile install leaves pnpm-lock.yaml unchanged.

### Actual behavior

Error: ERR_PNPM_OUTDATED_LOCKFILE

× installing dependencies ╰─▶ Cannot install with "frozen-lockfile" because pnpm-lock.yaml is not up to date with package.json.

    Failure reason:
    local dependency "lib" at "lib" is outdated

help: Regenerate the lockfile with pnpm install --lockfile-only so that pnpm-lock.yaml reflects the current package.json, then re-run pnpm install --frozen-lockfile.


### Additional information

This looks related to #16332 (fixed by #16346), but that fix doesn't cover this shape. In our real-world case (a monorepo using injectWorkspacePackages: true), the injected package (@ember-data/store) declares its peer via peerDependenciesMeta (optional), and the peer is provided by sibling importers elsewhere in the workspace — not by the importer that consumes the injected package directly. That combination still trips the freshness check on 12.8.1.

Removing peerDependenciesMeta (making the peer required, as in #16332's original repro) or removing app2's dependency on peer makes the frozen install pass, confirming the interaction between "injected package with an optional peer" and "a sibling importer supplying that peer" is what's still mishandled.

Because CI defaults to frozen installs, this continues to break CI for workspaces that inject a package with optional peer dependencies, even after #16346.

reproduces on at least Mac and Ubuntu

### Node.js version

24.19.0 and 26

### Operating System

Linux

### Contributing a fix

- [ ] I am working on a fix