Repository navigation
Vendored yarn berry revert restores the pre-vendor lock entry verbatim, so after upgrading from yarn 4.0.x it writes back a bare-hex checksum and every yarn install --immutable fails YN0028 #759
Description
[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).
Summary
vendor --revert and rollback restore a vendored yarn berry yarn.lock entry from the block recorded at vendor time, byte for byte (revert_recorded_block). Yarn 4.0.x writes checksum: as bare hex. Yarn 4.1+ writes it with the cache-key prefix (10c0/<hex>) and respells every entry on its next install, including the vendored file: entry, which socket-patch still accepts as its own. So if the project was vendored under yarn 4.0.x and then upgraded yarn (yarn set version, a packageManager bump, or corepack), the revert writes the old bare-hex line into a lock where every other entry is prefixed. Yarn 4.1+ then rejects the lock in every immutable install:
➤ YN0028: │ - checksum: 3fb59c76e281a2f5c810ad71dbbb8eba8b10c6cf94733dc7f27b8c516a5376cacea53543e76f6ae477d866c8954b27f1e15ca349424c2542474eb5bb1d2b6955
➤ YN0028: │ + checksum: 10c0/3fb59c76e281a2f5c810ad71dbbb8eba8b10c6cf94733dc7f27b8c516a5376cacea53543e76f6ae477d866c8954b27f1e15ca349424c2542474eb5bb1d2b6955
➤ YN0028: │ The lockfile would have been modified by this install, which is explicitly forbidden.
The revert exits 0 and reports vendoredReverted: ["pkg:npm/left-pad@1.3.0"]. The breakage only shows up in the next CI run.
The hosted twin is fine. Hosted rollback rebuilds the entry and calls checksum_in_lock_spelling on the live lock, so after the same upgrade it writes 10c0/<hex>, and a fresh hardened --immutable install passes. Only the vendored revert path ignores the lock's current spelling.
Impact
A team that vendored under yarn 4.0.x and later upgraded yarn can't cleanly unvendor: rollback or vendor --revert leaves a lock that breaks every CI install (enableImmutableInstalls is on by default in CI) until someone runs a mutable yarn install and commits the result.
Repro
Harness: yarn bundles from npm @yarnpkg/cli-dist@<v> (node package/bin/yarn.js), plus a local mock of the patch API and vendoring service serving a patched left-pad@1.3.0 tarball with a yarn-berry-zip yarnBerry10c0 artifact (the same shape as crates/socket-patch-cli/tests/e2e_vendor_yarn_berry_build.rs).
mkdir app && cd app
echo '{"name":"up","dependencies":{"left-pad":"^1.3.0","is-number":"^7.0.0"}}' > package.json
echo 'nodeLinker: node-modules' > .yarnrc.yml
yarn@4.0.2 install # lock: version 8, bare-hex checksums
socket-patch scan --mode vendored --yes --api-url $MOCK --org org --api-token x
yarn@4.18.1 install # yarn upgrade: lock version 10, every checksum respelled 10c0/<hex>
socket-patch rollback --api-url $MOCK --org org --api-token x # or: socket-patch vendor --revert
grep -A3 '"left-pad@' yarn.lock # checksum: 3fb59c76… (bare) among 10c0/ entries
mkdir f && cp package.json yarn.lock .yarnrc.yml f/ && cd f
yarn@4.18.1 install --immutable # exit 1, YN0028 on the left-pad checksum lineExpected vs actual
- Expected: after a revert, the lock is one yarn accepts under
--immutable, as docs/testing/yarn-berry-compatibility.md promises for vendored--revert. The restored entry should follow the live lock's checksum spelling, as the forward vendor path (yarn_berry_lock.rs:320) and hosted rollback (patch/redirect/upstream/npm.rs,checksum_in_lock_spelling(&content, …)) both do. - Actual: the restored entry keeps the spelling recorded under yarn 4.0.x, the revert exits 0, and every later
yarn install --immutablefails YN0028.
Matrix (Linux, node 22, main 045d7ec)
| vendored with | yarn after upgrade | revert command | fresh --immutable |
|---|---|---|---|
| 4.0.2 | 4.12.0 | vendor --revert |
fail YN0028 |
| 4.0.2 | 4.12.0 | rollback |
fail YN0028 |
| 4.0.2 | 4.18.1 | vendor --revert |
fail YN0028 |
| 4.0.2 | 4.18.1 | rollback (twice, separate projects) |
fail YN0028 |
| 4.0.2 | (none) | rollback |
pass (byte-exact, covered in earlier runs) |
| 4.0.2 (hosted) | 4.18.1 | rollback |
pass (writes 10c0/) |
Release 4.0.0 (npm) behaves the same (vendor --revert, then 4.18.1 --immutable fails), so this isn't a regression. Release 3.3.0 doesn't vendor this setup. macOS and Windows weren't probed (the routine can't currently push probe branches), but the code path doesn't depend on the OS.
Suspect code
crates/socket-patch-core/src/vendor/yarn_classic_lock.rs:646:replace_block(text, block, &original, …)writes the recordedoriginallines verbatim.crates/socket-patch-core/src/vendor/yarn_berry_lock.rs:755: the berry revert calls that shared helper with no re-spelling ofchecksum:. One possible fix is to pass the restored berry block'schecksum:throughchecksum_in_lock_spelling(<live lock>, …)before writing it.
Backlog review — 2026-10-08
Priority: P1 → P2. After a Yarn version upgrade the lock checksum spelling causes a visible failure; this is recoverable compatibility work.
Activity
mikolalysenko commentedon Oct 7, 2026
[agent] Yarn Berry bug-hunt run 29 (ledger #305) found a second symptom of the same verbatim restore, with no yarn upgrade involved. Main
05ecc6e, yarn 4.0.2 and 4.18.1, Linux, node-modules linker.If a new descriptor of the patched package is added after vendoring, the revert drops it. Example: a workspace member adds
"lpx": "1.0.0"while the root has^1.0.0. The name-keyedresolutionspin covers the new descriptor and installs patched bytes (correct), so the vendored lock has onefile:entry.vendor --revertandrollbackthen write back the pre-vendor"lpx@npm:^1.0.0":block alone. Both exit 0, and every freshyarn install --immutablefails YN0028:vendor --revertrollback--immutableYN0028Repro:
The restored entry key should be rebuilt from the lock's current descriptors for the package, not taken from the snapshot. That would also fix the checksum spelling this issue reports. The hosted pin has a related problem when a descriptor is added after pinning (re-scan dead end, and a rollback that writes duplicate blocks); that is filed as #1082.
Generated by Claude Code
mikolalysenko commentedon Oct 12, 2026
[agent] Re-checked on main
1779317(yarn 4.0.2 and 4.18.1, Linux). Both symptoms still fail:vendor --revert: the bare-hexchecksum:is written back, and every fresh--immutablefails YN0028."left-pad": "1.3.0"beside the root's^1.3.0after vendoring, thenvendor --revertorrollback: exit 0, the pre-vendor"left-pad@npm:^1.3.0":block comes back alone, and YN0028 follows (plain and hardened).#1397 (#1082) fixed the hosted side of the second symptom and the vendored alias variant (
lp: npm:left-pad@1.3.0added after vendoring, then revert: now passes). The vendored plain-member variant still goes through the verbatim restore.Generated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Yarn Berry bug-hunt run 39 (ledger #305) found a third symptom of the same verbatim restore: the vendored twin of #1435, which #1436 fixed only for hosted pins. Main
15c2c83, yarn 4.0.2 and 4.18.1, Linux, node-modules linker.If a workspace member removes its descriptor of the vendored package after vendoring (another member keeps it),
vendor --revert,rollbackandremove left-padall write back the pre-vendor merged block, including the removed member's descriptor. All three exit 0, and every freshyarn install --immutablefails YN0028:The vendored state itself was fine: the fresh
--immutableinstall right afteryarn removepassed.vendor --revertrollbackremove left-padRepro:
For a fix, the restore should key the entry by the descriptors that live lock entries still use, as #1436 now does for hosted (
restore_berry), and not by the block recorded at vendor time.Generated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Claiming this issue (shared root cause: the vendored yarn berry revert writes back the lock block recorded at vendor time verbatim instead of re-deriving its key and checksum spelling from the live lock). Branch: agent/fix-berry-vendored-verbatim-restore. Claim-ID: 2026-10-13T03:21:07Z-ce3a89
Generated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Draft PR: #1464
Generated by Claude Code
Rebuild vendored berry revert from the live lock
Harden the berry revert rebuild after review