Skip to content

Vendored pnpm refuses a user exact-pin override (left-pad: 1.3.0) in pnpm-workspace.yaml with a misleading "does not match package.json" error, though the same pin in package.json is taken over #854

Description

[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).

Summary

Vendored pnpm takes over a user's exact-version override of the package it vendors (pnpm_lock.rs OverrideDisposition::Takeover: "the user's pin already forces every tar-fs to this exact version, so redirecting the same key preserves their semantics"). That takeover only looks at package.json pnpm.overrides. On pnpm 10.5+, and always on pnpm 11 / 12 (which ignore package.json overrides), the override lives in pnpm-workspace.yaml. There, the same bare-key exact pin is refused with vendor_override_conflict:

pnpm-lock.yaml carries an override key left-pad for left-pad that does not match package.json's left-pad@1.3.0 — the two override maps must agree (run pnpm install to re-sync them) before vendoring

package.json has no overrides at all, and the lock and the workspace file already agree. The suggested pnpm install changes nothing, so the error points the user at the wrong file and the wrong fix.

The behaviour is inconsistent across key shapes and files:

  • bare key left-pad: 1.3.0 in package.json pnpm.overrides (pnpm 9.15.9): taken over, frozen install patched, revert restores it
  • versioned key left-pad@1.3.0: 1.3.0 in pnpm-workspace.yaml (10.34.5, 12.8.1): taken over, frozen install patched, revert byte-exact
  • bare key left-pad: 1.3.0 in pnpm-workspace.yaml (10.34.5, 11.28.3, 12.8.1): refused

Impact

On pnpm 11 / 12, exact-pinning a vulnerable transitive dependency in pnpm-workspace.yaml overrides: is the standard way to force a version, and vendoring can't patch that package. The refusal is loud and writes nothing for that package. But from a hosted project the takeover un-hosts it first (#853), and the error sends users to package.json and pnpm install, which don't help.

Repro (Linux, Node 22, main 4646693)

I used a local mock patch API that serves left-pad@1.3.0 with a marker prepended to index.js.

API="--api-url http://127.0.0.1:8787 --org acme --api-token x"
mkdir p && cd p
echo '{"name":"c","version":"1.0.0","dependencies":{"left-pad":"1.3.0"}}' > package.json
printf 'packages:\n  - .\noverrides:\n  left-pad: 1.3.0\n' > pnpm-workspace.yaml
pnpm install            # the lock records `overrides: left-pad: 1.3.0`
socket-patch scan --mode vendored --json --yes $API
# exit 1, partial_failure, left-pad: failed vendor_override_conflict ("… does not match package.json's `left-pad@1.3.0` …")
# change the key to `left-pad@1.3.0: 1.3.0` → exit 0, the value becomes the file: spec, frozen install patched

Expected vs actual

Matrix (vendored scan exit / outcome)

pnpm bare key in pnpm-workspace.yaml versioned key in pnpm-workspace.yaml bare key in package.json
9.15.9 1 / refused not run 0 / taken over, frozen patched
10.34.5 1 / refused 0 / taken over, frozen patched, revert byte-exact not run
11.28.3 1 / refused not run n/a (pnpm 11 ignores package.json overrides)
12.8.1 1 / refused 0 / taken over, frozen patched, revert byte-exact n/a

Each refused cell was reproduced at least twice. I tested Linux only (no OS-specific code is involved). Not a regression: release 4.0.0 gives the same refusal and message on 12.8.1.

Suspect code

  • crates/socket-patch-core/src/vendor/pnpm_lock.rs:617: classify_pkg_override derives effective_key from package.json alone, so with no package.json override it is our canonical left-pad@1.3.0.
  • pnpm_lock.rs:1404 (check_lock_override) then sees the lock's left-pad key ≠ effective_key and refuses with the package.json wording. pnpm_lock.rs:1852 (check_workspace_override) would refuse it the same way.
  • A fix would classify the workspace overrides: section with the same Takeover / Ours / conflict rules when it holds the override.

Backlog review — 2026-10-08

Priority: P1 → P2. Existing pnpm override shapes cause a refusal; failures are visible rather than unsafe successful patch claims.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions