[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).
Summary
Yarn 4 writes a lock entry's fields in a fixed order: version, resolution, dependencies, peerDependencies, dependenciesMeta, peerDependenciesMeta, bin, checksum, conditions, languageName, linkType. Platform-specific packages carry a conditions: line (conditions: os=linux & cpu=x64 & libc=glibc), and when yarn reaches them only through optionalDependencies (esbuild → @esbuild/linux-x64, sharp → @img/sharp-linux-x64) it writes the entry without a checksum: line. Both berry pins place the checksum somewhere else:
- Vendored (
vendor / scan --mode vendored) rebuilds the entry as version, resolution, carried sections, checksum, languageName, linkType. conditions: is a carried section, so it lands before checksum:. Every conditional entry is affected, including a direct dependency whose original entry already had a checksum.
- Hosted (
scan --mode hosted), when the original entry has no checksum: line, inserts one directly after resolution: (patch/redirect/mod.rs ~3806). That's before dependencies: / dependenciesMeta: / bin:, so any conditional entry with dependencies (every @img/sharp-* platform package) is affected.
Yarn's immutable check compares the lock text, so the misplaced line alone fails --immutable (YN0028; the diff is the same checksum line moved). scan exits 0 with status: success, and vendor --check reports vendor_check_ok.
Impact
Any project that vendors or hosts a patch for a platform-native package (@esbuild/*, @img/sharp-*, @rollup/rollup-*, @swc/core-*, lightningcss-*, @parcel/watcher-*, …) gets a committed lock that fails every CI yarn install --immutable (yarn turns immutable on by itself in CI). A plain yarn install "fixes" it by reordering the line. That's the opposite of the documented contract that the pin survives a fresh yarn install --immutable --check-cache.
Repro (yarn 4.18.1; local mock patch API serving a README-only patch for @img/sharp-linux-x64@0.33.5, yarnBerry10c0 from a real resolutions: file: bootstrap install)
mkdir p && cd p
echo '{"name":"dd","version":"1.0.0","private":true,"dependencies":{"sharp":"0.33.5"}}' > package.json
printf 'nodeLinker: node-modules\n' > .yarnrc.yml
yarn install # lock entry "@img/sharp-linux-x64@npm:0.33.5": dependencies / dependenciesMeta / conditions, no checksum
git init -q && git add -A && git commit -qm init
socket-patch scan --mode vendored --json --yes <api flags>; echo $? # 0, success (or: --mode hosted)
git clone -q . ../fresh && cp -r .socket ../fresh/ 2>/dev/null; cd ../fresh
yarn install --immutable --check-cache; echo $? # 1
# ➤ YN0028: │ + checksum: 10c0/0ab5bac1… (where yarn wants it: after dependenciesMeta, before conditions)
# ➤ YN0028: │ - checksum: 10c0/0ab5bac1… (where socket-patch wrote it)
# ➤ YN0028: │ The lockfile would have been modified by this install, which is explicitly forbidden.
Lock entry as written (vendored):
"@img/sharp-linux-x64@file:./.socket/vendor/npm/<uuid>/@img/sharp-linux-x64-0.33.5.tgz::locator=dd%40workspace%3A.":
version: 0.33.5
resolution: "@img/sharp-linux-x64@file:…"
dependencies: …
dependenciesMeta: …
conditions: os=linux & cpu=x64 & libc=glibc
checksum: 10c0/0ab5bac1… # yarn: before `conditions:`
languageName: node
linkType: hard
Hosted writes checksum: between resolution: and dependencies: instead.
Expected vs actual
- Expected: docs/testing/yarn-berry-compatibility.md says the hosted pin is "what yarn itself writes for a root
resolutions entry", and that a fresh yarn install --immutable --check-cache "passes … and leaves the lock untouched". Vendored "wires the root package.json resolutions plus the lock's file: entry" as "the exact entry yarn 4 emits" (comment at vendor/yarn_berry_lock.rs:349). Both should put checksum: where yarn does: after bin: / the dependency maps, before conditions:.
- Actual: the checksum is misplaced, so every immutable install fails YN0028 while socket-patch reports success.
Matrix (Linux, main 045d7ec, Node 22; each cell is a fresh fixture + fresh-checkout yarn install --immutable --check-cache)
| yarn |
fixture |
vendored |
hosted |
| 4.0.2 (bare-hex checksums) |
sharp@0.33.5 (transitive @img/sharp-linux-x64, deps, no checksum) |
YN0028 |
YN0028 |
| 4.6.0 |
same |
YN0028 |
YN0028 |
| 4.12.0 |
same |
YN0028 |
YN0028 |
| 4.18.1 |
same (×2) |
YN0028 |
YN0028 |
| 4.18.1 |
esbuild@0.19.12 (transitive @esbuild/linux-x64, no deps, no checksum) |
YN0028 |
pass (the line after resolution: happens to be yarn's spot) |
| 4.18.1 |
direct @esbuild/linux-x64@0.19.12 (checksum present) |
YN0028 |
pass (existing line replaced in place) |
4.18.1, supportedArchitectures: {os: [darwin], cpu: [arm64]} |
esbuild@0.19.12 |
YN0028 |
pass |
Every failing cell's YN0028 diff is only the moved checksum: line. Non-conditional packages (left-pad, debug, ms, …) pass in both modes, which is why the existing suites are green.
Not a regression: release 4.0.0 fails the same sharp cell in both modes (its hosted pin is the older ::__archiveUrl= form, with the same insertion). Release 3.3.0 predates these flags.
Suspect code
- Vendored:
crates/socket-patch-core/src/vendor/yarn_berry_lock.rs:351-360 builds version, resolution, carried…, checksum, languageName, linkType. carried_sections (~1301) treats conditions: as carried, so it's emitted before checksum:. conditions needs to come after the checksum.
- Hosted:
crates/socket-patch-core/src/patch/redirect/mod.rs:3806-3815. The Some(checksum) arm with no existing checksum: line inserts it right after resolution:. It should go before conditions: / languageName: (after bin: and the dependency maps).
- Related, smaller: hosted
rollback (patch/redirect/upstream/npm.rs restore_berry) keeps the inserted checksum: line on an entry that originally had none, so the restored lock isn't byte-identical to the pre-pin lock. Yarn accepts it under --immutable, and the next mutable yarn install removes it again.
vendor --check (vendor_check_ok) doesn't catch the misplaced line.
Not OS-specific (lock-text ordering), so no probe run. On Windows the lock is CRLF, but the same line order applies.
[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).
Summary
Yarn 4 writes a lock entry's fields in a fixed order:
version,resolution,dependencies,peerDependencies,dependenciesMeta,peerDependenciesMeta,bin,checksum,conditions,languageName,linkType. Platform-specific packages carry aconditions:line (conditions: os=linux & cpu=x64 & libc=glibc), and when yarn reaches them only throughoptionalDependencies(esbuild→@esbuild/linux-x64,sharp→@img/sharp-linux-x64) it writes the entry without achecksum:line. Both berry pins place the checksum somewhere else:vendor/scan --mode vendored) rebuilds the entry asversion,resolution, carried sections,checksum,languageName,linkType.conditions:is a carried section, so it lands beforechecksum:. Every conditional entry is affected, including a direct dependency whose original entry already had a checksum.scan --mode hosted), when the original entry has nochecksum:line, inserts one directly afterresolution:(patch/redirect/mod.rs~3806). That's beforedependencies:/dependenciesMeta:/bin:, so any conditional entry with dependencies (every@img/sharp-*platform package) is affected.Yarn's immutable check compares the lock text, so the misplaced line alone fails
--immutable(YN0028; the diff is the same checksum line moved).scanexits 0 withstatus: success, andvendor --checkreportsvendor_check_ok.Impact
Any project that vendors or hosts a patch for a platform-native package (
@esbuild/*,@img/sharp-*,@rollup/rollup-*,@swc/core-*,lightningcss-*,@parcel/watcher-*, …) gets a committed lock that fails every CIyarn install --immutable(yarn turns immutable on by itself in CI). A plainyarn install"fixes" it by reordering the line. That's the opposite of the documented contract that the pin survives a freshyarn install --immutable --check-cache.Repro (yarn 4.18.1; local mock patch API serving a README-only patch for
@img/sharp-linux-x64@0.33.5,yarnBerry10c0from a realresolutions: file:bootstrap install)Lock entry as written (vendored):
Hosted writes
checksum:betweenresolution:anddependencies:instead.Expected vs actual
resolutionsentry", and that a freshyarn install --immutable --check-cache"passes … and leaves the lock untouched". Vendored "wires the rootpackage.jsonresolutionsplus the lock'sfile:entry" as "the exact entry yarn 4 emits" (comment atvendor/yarn_berry_lock.rs:349). Both should putchecksum:where yarn does: afterbin:/ the dependency maps, beforeconditions:.Matrix (Linux, main
045d7ec, Node 22; each cell is a fresh fixture + fresh-checkoutyarn install --immutable --check-cache)sharp@0.33.5(transitive@img/sharp-linux-x64, deps, no checksum)esbuild@0.19.12(transitive@esbuild/linux-x64, no deps, no checksum)resolution:happens to be yarn's spot)@esbuild/linux-x64@0.19.12(checksum present)supportedArchitectures: {os: [darwin], cpu: [arm64]}esbuild@0.19.12Every failing cell's YN0028 diff is only the moved
checksum:line. Non-conditional packages (left-pad, debug, ms, …) pass in both modes, which is why the existing suites are green.Not a regression: release 4.0.0 fails the same
sharpcell in both modes (its hosted pin is the older::__archiveUrl=form, with the same insertion). Release 3.3.0 predates these flags.Suspect code
crates/socket-patch-core/src/vendor/yarn_berry_lock.rs:351-360buildsversion, resolution, carried…, checksum, languageName, linkType.carried_sections(~1301) treatsconditions:as carried, so it's emitted beforechecksum:.conditionsneeds to come after the checksum.crates/socket-patch-core/src/patch/redirect/mod.rs:3806-3815. TheSome(checksum)arm with no existingchecksum:line inserts it right afterresolution:. It should go beforeconditions:/languageName:(afterbin:and the dependency maps).rollback(patch/redirect/upstream/npm.rsrestore_berry) keeps the insertedchecksum:line on an entry that originally had none, so the restored lock isn't byte-identical to the pre-pin lock. Yarn accepts it under--immutable, and the next mutableyarn installremoves it again.vendor --check(vendor_check_ok) doesn't catch the misplaced line.Not OS-specific (lock-text ordering), so no probe run. On Windows the lock is CRLF, but the same line order applies.