A custom fetch done during resolution is filed under an identity the install pass does not derive, so the hook runs twice
pnpm version
12.4.2
Code to reproduce the issue
CustomFetcherSession::completed keys a verified fetch by (package_id, integrity) so that a fetch done during resolution is reused during installation instead of running the hook a second time. The two phases derive package_id differently:
- Resolution, in
PrefetchingResolver, usesResolvedPackageInfo::name_verwhen the resolver filled it and falls back to the tarball URL otherwise. - Installation derives the
pkg_idfrom the lockfile entry, which for a registry-shaped package isname@version.
A resolver that leaves name_ver unset — every pnpmfile resolvers hook does, see CustomResolverAdapter::resolved_hook_result — therefore files its entry under the URL while the install pass looks it up under name@version. The lookup misses and the custom fetcher runs again.
Expected behavior
One custom fetch per package per install.
Actual behavior
Two, whenever the resolve-time custom fetch path runs for a resolver result with no name_ver.
Additional information
Noticed in review of #15013. The mismatch predates that PR — populate_missing_integrity already used the same fallback — but it only applied to tarballs with no integrity. #15013 also reads an already-pinned archive when the resolver supplied no manifest, so more resolutions now take the path.
The remedy is to make the two phases agree on one identity. Registering the completed fetch under the name@version read from the fetched manifest is the smaller version of that; the durable version is a single resolution-plus-integrity identity both phases derive. Either way it needs a pnpmfile declaring both a resolvers and a fetchers hook to test, which is why it was not carried in that PR.
Related: #15021 is the same shape of defect in the tarball mem cache, where resolve-time publication and install-time lookup disagree about what identifies an extraction.
Node.js version
24.18.1
Operating System
macOS
Written by an agent (Claude Code, claude-opus-5).
Source: pnpm/pnpm