Repository navigation
Hosted and vendored pnpm 11/12 refuse a standalone project nested under an unrelated pnpm-workspace.yaml (not in its packages: globs) as a "workspace member", and the suggested fix doesn't work (regression from #888) #1006
Description
[agent] Found by the scheduled pnpm bug-hunt routine (ledger #303).
Summary
Since #888 (fix for #880 / #881), hosted and vendored modes refuse any pnpm project with its own v9 pnpm-lock.yaml and no pnpm-workspace.yaml of its own whenever any ancestor directory has a pnpm-workspace.yaml. governing_workspace_file takes the nearest ancestor file without checking that the project matches that file's packages: globs.
pnpm 11 and 12 don't treat such a directory as a workspace project. Running pnpm install in examples/demo (not matched by the root's packages: [packages/*]) writes examples/demo/pnpm-lock.yaml and reads only a pnpm-workspace.yaml in examples/demo, never the root one. This is a common layout: examples/, e2e/ or docs/ apps outside the globs, or a checkout inside a parent folder that happens to hold a pnpm-workspace.yaml.
So on main:
- hosted exits 1 with
redirect_pnpm_settings_elsewhere. Its remedy ("addtrustLockfile: trueto /pnpm-workspace.yaml") does nothing: the frozen install still fails withERR_PNPM_TARBALL_URL_MISMATCH. - vendored fails each package with
vendor_pnpm_settings_elsewhereand tells the user to use--mode hosted, which also refuses. Neither mode can patch the project. - Release 4.0.0 patches the same project in both modes. The nested
pnpm-workspace.yamlit creates (which the new refusal says "would be ignored") is exactly the file pnpm reads, and a fresh frozen install lands patched bytes.
Impact
Every pnpm 11/12 project nested below an unrelated workspace file stops being patchable in hosted and vendored modes, and both error messages point at remedies that don't work.
Repro (pnpm 12.10.1, Node 22, mock patch API as in the pnpm compatibility e2e)
mkdir -p ws/packages/a ws/examples/demo && cd ws
printf 'packages:\n - packages/*\n' > pnpm-workspace.yaml
echo '{"name":"root","private":true}' > package.json
echo '{"name":"a","version":"1.0.0"}' > packages/a/package.json
echo '{"name":"demo","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0","ms":"2.1.3"}}' > examples/demo/package.json
cd examples/demo && pnpm install # pnpm 11/12: lock written to examples/demo/pnpm-lock.yaml
socket-patch scan --mode hosted --json --yes # exit 1, redirect_pnpm_settings_elsewhere
socket-patch scan --mode vendored --json --yes # exit 1, vendor_pnpm_settings_elsewhere x2
# Proof that the root file doesn't govern demo (pins written with --no-trust-lockfile-config,
# then a fresh frozen install of a copy, empty store):
# C: no trust anywhere -> ERR_PNPM_TARBALL_URL_MISMATCH
# A: `trustLockfile: true` in ws/pnpm-workspace.yaml -> ERR_PNPM_TARBALL_URL_MISMATCH (the advised fix)
# B: `trustLockfile: true` in examples/demo/pnpm-workspace.yaml -> exit 0, left-pad patchedExpected vs actual
CLI_CONTRACT.md scopes both codes to "a pnpm workspace member with its own … pnpm-lock.yaml (sharedWorkspaceLockfile: false)". examples/demo isn't a member: pnpm doesn't list it as a workspace project, gives it its own lock, and reads its own pnpm-workspace.yaml. Expected: hosted pins the lock and creates examples/demo/pnpm-workspace.yaml with trustLockfile: true, and vendored wires the override there, as 4.0.0 does. Actual: both modes refuse, and nothing is written.
OS × version
| OS | pnpm | layout (lock location) | hosted on main | vendored on main | 4.0.0 |
|---|---|---|---|---|---|
| Linux | 12.10.1 | own lock in examples/demo |
refused (reproduced twice), advised fix fails, nested file works | refused | hosted + vendored pass, fresh frozen install patched |
| Linux | 11.28.5 | own lock in examples/demo |
refused, advised fix fails, nested file works | not run | not run |
| Linux | 10.34.6 / 9.15.9 | pnpm installs the root workspace instead (no lock in demo) |
n/a | n/a | n/a |
macOS and Windows weren't probed. The logic is path-only and OS-independent.
First bad commit
8e8daf0 (#888). Release 4.0.0 is good.
Suspect code
crates/socket-patch-core/src/utils/pnpm_workspace.rs:27(governing_workspace_file): any ancestor file is treated as governing; the project isn't matched against itspackages:globs (or the root itself).crates/socket-patch-core/src/hosted/governing_root.rs:139(pnpm_settings_elsewhere)crates/socket-patch-core/src/vendor/pnpm_lock.rs:594(vendored refusal)
No probe runs; Linux only.
Backlog review — 2026-10-08
Priority: P1 → P2. An unrelated ancestor workspace causes a false refusal. Real pnpm usability regression with a narrow nesting trigger.
Activity
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
mikolalysenko commentedon Oct 7, 2026
[agent] Triage:
priority:p1. pnpm. Already claimed by draft #1007.Generated by Claude Code
Fix pnpm projects outside the workspace globs refused as members (#1006)
Keep pnpm workspace globs off dot directories (#1006)
Merge branch 'pnpm-fix/b3-lockselect' into agent/fix-pnpm-open-issues
Address review findings on the pnpm open-issues branch
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.mikolalysenko commentedon Oct 8, 2026
[agent] New variant from the pnpm bug-hunt routine (ledger #303), main
cf8b164, Linux, reproduced twice: a settings-only parentpnpm-workspace.yamlwith nopackages:key gives the same wrong refusal. PR #1007's regression tests use parents that do havepackages:globs, so this shape may need its own case.Layout:
parent/pnpm-workspace.yamlcontains onlyminimumReleaseAge: 0, andparent/child/is a standalone project depending onleft-pad@1.3.0. Runningpnpm installinchild/on pnpm 11.28.5 and 12.10.1 writeschild/pnpm-lock.yaml, not a lock at the parent, so pnpm treats the child as its own root.scan --mode hostedinchild/scan --mode vendoredtrustLockfile: truein the parent file) → fresh--frozen-lockfiletrustLockfile: trueinchild/pnpm-workspace.yaml→ fresh--frozen-lockfileredirect_pnpm_settings_elsewhereERR_PNPM_TARBALL_URL_MISMATCHredirect_pnpm_settings_elsewherevendor_pnpm_settings_elsewhereERR_PNPM_TARBALL_URL_MISMATCHSo, as in the original report, the refusal sends the user to a file pnpm doesn't read for this project. A parent file with no
packages:key should probably count as "not a member" for pnpm ≥ 11, just like a parent whose globs don't match.On pnpm 10.34.6 the same layout installs nothing in
child/(no lock, nonode_modules). Hosted and vendored then exit 0 with nothing to do, so pnpm 10 isn't affected.Generated by Claude Code
mikolalysenko commentedon Oct 9, 2026
[agent] pnpm bug-hunt, another variant on main
03b9418: a project excluded by a negated glob.packages/excluded(left-pad@1.3.0) gets its ownpnpm-lock.yamlfrompnpm installrun inside it, and the root lock has noleft-pad. pnpm treats it as outside the workspace.scan --mode hostedfrompackages/excludedscan --mode vendoredredirect_pnpm_settings_elsewhere, nothing writtenredirect_pnpm_settings_elsewhere, nothing writtenvendor_pnpm_settings_elsewhereThe refusal's remedy is wrong here too (12.10.1, pinned with
--no-trust-lockfile-config, then a frozen install with an empty store):trustLockfile: trueappended to the rootpnpm-workspace.yaml, as the message suggests:ERR_PNPM_TARBALL_URL_MISMATCH.trustLockfile: truein a newpackages/excluded/pnpm-workspace.yaml, which the message says "would be ignored": the install succeeds andleft-padis patched.So the membership check needs to honour
!patterns inpackages:as well as plain globs.Generated by Claude Code