Repository navigation
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-padforleft-padthat does not match package.json'sleft-pad@1.3.0— the two override maps must agree (runpnpm installto 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.0inpackage.jsonpnpm.overrides(pnpm 9.15.9): taken over, frozen install patched, revert restores it - versioned key
left-pad@1.3.0: 1.3.0inpnpm-workspace.yaml(10.34.5, 12.8.1): taken over, frozen install patched, revert byte-exact - bare key
left-pad: 1.3.0inpnpm-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 patchedExpected vs actual
- Expected: the workspace file is treated like
package.jsonfor overrides. An exact pin equal to the vendored version, under either the bare or thename@versionkey, is taken over in place, andvendor --revertrestores the user's value. That's howclassify_pkg_overridehandlespackage.json, and on pnpm ≥10.5 the workspace file is the authoritative override map (Vendored pnpm 10.5+ withoverrides:in pnpm-workspace.yaml: the new package.jsonpnpm.overridesshadows the user's overrides, so frozen installs fail and a re-lock drops them #360 / Fix vendored pnpm shadowing workspace overrides (#360) #785). A genuine conflict (a range, a different version or a selector) stays a refusal, and its message names the file that actually carries the override. CLI_CONTRACT.md listsvendor_override_conflictonly for "a user-authored override/resolution … already exists"; the takeover exception exists in the code but not in the docs. - Actual: the bare-key workspace pin is refused, and the detail blames
package.json.
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_overridederiveseffective_keyfrompackage.jsonalone, so with nopackage.jsonoverride it is our canonicalleft-pad@1.3.0.pnpm_lock.rs:1404(check_lock_override) then sees the lock'sleft-padkey ≠effective_keyand refuses with thepackage.jsonwording.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
mikolalysenko commentedon Oct 5, 2026
[agent] Triage: priority:p1. pnpm (npm-family). The override takeover only reads package.json pnpm.overrides and misses a bare-key exact pin in pnpm-workspace.yaml. This is also one of the three triggers in #853, but #853's root cause (takeover restores the hosted pin before the vendored gates run) is separate, so they are cross-linked rather than clustered.
Generated by Claude Code
mikolalysenko commentedon Oct 7, 2026
[agent] Claiming this issue as part of the pnpm open-issue sweep in draft PR #1007. Branch: agent/fix-pnpm-open-issues. Claim-ID: 2026-10-07T12:44:29Z-pnpm07
Fix vendored pnpm refusing a pnpm-workspace.yaml exact-pin override (#…
Take over a quoted or commented pnpm-workspace.yaml exact pin (#854)
mikolalysenko commentedon Oct 7, 2026
[agent] PR #1007 fixes this issue. It has a regression test that fails on
main, CI is fully green (552 checks) and it's ready for review. The issue will close when #1007 merges. The PR description's table gives the root cause, the fix and the test for each issue.