Repository navigation
Hosted yarn berry rollback/remove drop the ::__archiveUrl= binding when the project's mirror serves npmjs tarball URLs, so cold installs fetch a mirror path that 404s #1465
Description
[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).
Summary
Hosted rollback and remove rebuild a yarn berry lock entry from the version document of the registry yarn uses for the package. Since #908/#1017 that is the right registry (top-level npmRegistryServer, npmScopes.<scope>.npmRegistryServer, env, parent/home rc). But registry_derives_tarball (crates/socket-patch-core/src/patch/redirect/upstream/npm.rs:444) treats a dist.tarball as one yarn derives itself if it is the conventional URL under either the project's registry or npm_registry_base() (npmjs unless SOCKET_NPM_REGISTRY is set). Yarn compares only against the package's own registry (NpmSemverFetcher.isConventionalTarballUrl uses getScopeRegistry).
So with a metadata mirror whose packuments keep the original https://registry.npmjs.org/<name>/-/<name>-<v>.tgz tarball URLs, the case is:
- yarn writes
resolution: "left-pad@npm:1.3.0::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2F…". - After a hosted pin,
rollback/removerestoreresolution: "left-pad@npm:1.3.0"with no binding, and exit 0. - Yarn then fetches the conventional URL under the mirror,
<mirror>/left-pad/-/left-pad-1.3.0.tgz. A mirror that doesn't host tarballs at that path (which is why it handed out npmjs URLs) answers 404, and every cold install fails:
➤ YN0035: │ left-pad@npm:1.3.0: Package not found
➤ YN0035: │ Response Code: 404 (Not Found)
➤ YN0035: │ Request URL: http://127.0.0.1:8790/meta/left-pad/-/left-pad-1.3.0.tgz
#908 and #1017 were verified with a mirror whose tarballs live on the mirror's own non-conventional path, which this exemption doesn't touch. That's why those fixes pass and this shape doesn't.
Impact
Projects behind a registry mirror or proxy that passes npmjs metadata through unchanged (tarball URLs still point at registry.npmjs.org) can't install from a cold cache after unpinning a hosted patch. CI breaks after a command that reported success. A plain --immutable and --refresh-lockfile don't notice the changed locator; only the fetch fails.
Repro
Harness: yarn from npm @yarnpkg/cli-dist@<v>. A local mock of the patch API (hosted pin + /upstream/npm/<uuid>.json). A local "metadata mirror" at /meta/ that proxies npmjs packuments verbatim and 404s every /-/ tarball path.
mkdir app && cd app
echo '{"name":"app","private":true,"dependencies":{"@sindresorhus/is":"^4.6.0","left-pad":"^1.3.0"}}' > package.json
cat > .yarnrc.yml <<'EOF'
nodeLinker: node-modules
unsafeHttpWhitelist: ["127.0.0.1"]
npmRegistryServer: "http://127.0.0.1:8790/meta"
EOF
yarn install # lock: left-pad@npm:1.3.0::__archiveUrl=https%3A%2F%2Fregistry.npmjs.org%2F…
git init && git add -A && git commit -m base
# fresh checkout + cold cache: yarn install --immutable -> OK (baseline)
socket-patch scan --mode hosted --yes --api-url $MOCK --api-token x --org org --patch-server-url $MOCK
socket-patch rollback --yes --api-url $MOCK --api-token x --org org --patch-server-url $MOCK # exit 0
git diff -- yarn.lock # both __archiveUrl bindings gone
# fresh checkout + cold cache: yarn install --immutable -> YN0035 404 on <mirror>/left-pad/-/left-pad-1.3.0.tgzThe npmScopes form fails the same way: npmScopes: {sindresorhus: {npmRegistryServer: "http://127.0.0.1:8790/meta"}} (block style), with the scoped @sindresorhus/is as the target. remove <name> behaves like rollback.
Expected vs actual
- Expected: rollback/remove restore the lock to what yarn wrote (byte-exact, CLI_CONTRACT rollback), keeping
::__archiveUrl=whenever yarn would, i.e. wheneverdist.tarballisn't the conventional URL under that package's yarn registry. This is what Hosted yarn berry rollback/remove rebuilds the lock entry as a barename@npm:<version>locator and drops the registry's::__archiveUrl=binding, so projects on registries with non-conventional tarball URLs can't install after a revert #817/Hosted yarn berry rollback/remove still drops a custom registry's::__archiveUrl=binding (#817 fix incomplete): the restore looks updist.tarballon npmjs, not the project'snpmRegistryServer, so cold installs 404 #908/Hosted yarn berry rollback/remove still drops a mirror's::__archiveUrl=binding when the registry comes fromnpmScopes,YARN_NPM_REGISTRY_SERVER,~/.yarnrc.ymlor a parent-dir.yarnrc.yml(#908 fix covers only the project rc's top-level key) #1017 established. - Actual: the binding is dropped whenever the tarball happens to be conventional under npmjs, even though the project never resolves from npmjs. The command exits 0, and the next cold install 404s.
Matrix (Linux, node 22, main 15c2c83)
| yarn | registry source | rollback |
remove <name> |
|---|---|---|---|
| 4.0.2 | top-level npmRegistryServer |
exit 0, binding dropped, cold --immutable YN0035 |
same |
| 4.0.2 | npmScopes.<scope> (scoped target) |
same | same |
| 4.18.1 | top-level npmRegistryServer |
same | same |
| 4.18.1 | npmScopes.<scope> |
same | same |
Baseline (before pinning) cold --immutable passes in every cell. Control: with a mirror whose packuments point at its own non-conventional path, the restore keeps the binding (as verified for #1017). macOS/Windows not probed: the routine's probe branches are blocked. The logic is platform-independent.
Suspect code
crates/socket-patch-core/src/patch/redirect/upstream/npm.rs:444-458 (registry_derives_tarball). The npm_registry_base() arm makes an npmjs-conventional URL count as derived whatever the project's registry is. The unit test project_registry_decides_a_mirrors_tarball_urls only covers a mirror that hands back its own URLs. The same helper decides pnpm's tarball: restore (npm.rs:1846) and the line-1484 check, so pnpm projects behind the same kind of mirror may be affected too (not tested here).
Activity
mikolalysenko commentedon Oct 13, 2026
[agent] Triage: priority:p1 (yarn berry, npm family). Confirmed on main 15c2c83:
registry_derives_tarball(crates/socket-patch-core/src/patch/redirect/upstream/npm.rs:444) also accepts a URL that is conventional undernpm_registry_base(), while yarn and pnpm compare only against the package's own registry. Not a duplicate; no open PR covers it. No other open issue shares this cause; the pnpmtarball:restore (npm.rs:1846) and the pnpm derived check (npm.rs:1484) go through the same helper and will be covered by the same fix.Generated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Claiming this issue (shared root cause: tarball-derivation check also accepts npmjs-conventional URLs regardless of the project's registry). Branch: agent/fix-registry-derived-tarball-base. Claim-ID: 2026-10-13T04:20:40Z-fde1ce
Generated by Claude Code
mikolalysenko commentedon Oct 13, 2026
[agent] Draft PR: #1466
Generated by Claude Code
Keep mirror archiveUrl bindings on unpin
Add real-yarn e2e for mirror rollback
Tidy tarball check and cover pnpm evidence
chore(release): 5.0.1 (#1485)