Skip to content

Yarn 4 node-modules / pnpm-linker projects migrated from Yarn 2 PnP keep a stale .pnp.js, and socket-patch refuses them as Plug'n'Play: agent and vendored exit 1, hosted warns "npm dependencies were NOT scanned" (regression since 3.3.0) #975

Description

[agent] Found by the scheduled Yarn Berry (2+) bug-hunt routine (ledger #305).

Summary

Yarn 2 wrote its PnP loader as .pnp.js. When a Yarn 2 PnP project upgrades to Yarn 4 and switches to nodeLinker: node-modules (or pnpm), Yarn 4 never deletes the old .pnp.js. Yarn 4.0.2 and 4.18.1 both leave it in place, while Yarn 2.4.2 and 3.8.7 delete it when they switch linkers. Yarn ignores the file: require.resolve('left-pad') and yarn node resolve into node_modules, process.versions.pnp is unset, and yarn install --immutable passes.

socket-patch treats any root .pnp.js as a live Yarn 2 PnP layout (PNP_MARKERS = [".pnp.cjs", ".pnp.js", ".pnp.loader.mjs"]) and never checks the configured linker or the installed node_modules/.yarn-state.yml. As a result, a project that installs normally into node_modules is refused:

  • agent: scan / apply exit 1 with Error: yarn-berry Plug'n'Play layout is not supported … Packages live inside .yarn/cache/*.zip, and node_modules/left-pad stays unpatched.
  • vendored: exit 1, Cannot vendor …: found .pnp.js: this is a yarn berry Plug'n'Play project.
  • hosted: the pin is written and works, but every run prints the yarn_pnp_unsupported warning ("npm dependencies were NOT scanned … cannot discover or patch them in ANY mode"), which is false.

Impact

On these projects, agent and vendored modes can't patch anything, and they report it with a wrong diagnosis: the error tells the user to switch to yarn patch. Hosted mode tells the user that nothing was scanned even though it pinned the package. Release 3.3.0 patched the same tree correctly.

Repro

mkdir p && cd p
echo '{"name":"t","private":true,"dependencies":{"left-pad":"1.3.0"}}' > package.json
yarn@2.4.2 install                                   # Yarn 2 PnP project -> writes .pnp.js
printf 'nodeLinker: node-modules\ncompressionLevel: 0\n' > .yarnrc.yml
rm yarn.lock && yarn@4.18.1 install                  # upgrade + switch linker (4.0.2 behaves the same)
ls -a | grep pnp                                     # .pnp.js is still there
node -e "console.log(require.resolve('left-pad'))"   # -> ./node_modules/left-pad/index.js
socket-patch scan --mode agent --yes                 # exit 1: "yarn-berry Plug'n'Play layout is not supported"
socket-patch scan --mode vendored --yes              # exit 1: "found `.pnp.js`: … Plug'n'Play project"
socket-patch scan --mode hosted --yes                # pins, but warns yarn_pnp_unsupported "NOT scanned"
rm .pnp.js && socket-patch scan --mode agent --yes   # control: exit 0, 1/1 applied

(Patch data came from a local mock of the patch API serving a left-pad 1.3.0 patch. Yarn ran as node <@yarnpkg/cli-dist@X>/bin/yarn.js.)

Expected vs actual

  • Expected: per docs/ecosystems.md, berry is supported on the node-modules linker and only PnP is refused. The PnP refusal is meant for trees where "packages aren't on disk" (the comment at crates/socket-patch-core/src/crawlers/pkg_managers.rs:98). Here, packages are on disk and the configured linker is node-modules, so the refusal should not fire. A refusal that fires when it shouldn't is a bug.
  • Actual: agent and vendored mode refuse with exit 1, and hosted mode prints a false "NOT scanned" warning.

Matrix (Linux, Node 22)

Yarn Linker agent vendored hosted control (no .pnp.js)
4.18.1 node-modules fail (exit 1) fail (exit 1) pins, false warning agent pass
4.18.1 pnpm fail (exit 1) fail (exit 1) pins, false warning agent pass
4.0.2 node-modules fail (exit 1) fail (exit 1) pins, false warning agent pass
4.0.2 pnpm fail (exit 1) fail (exit 1) pins, false warning agent pass
2.4.2 / 3.8.7 (linker switch) node-modules not reachable: Yarn deletes .pnp.js

Each cell was reproduced on main 9c43dfc, from a fresh Yarn 2 → Yarn 4 migration. macOS and Windows weren't probed, because probe branches are blocked for this routine. The detection is a plain is_file() on the project root, so it shouldn't depend on the OS.

First bad release

On the same tree, release 3.3.0 (socket-patch scan) applies the patch, exit 0, /* SOCKET-PATCHED */. Release 4.0.0 refuses it (yarn-berry Plug'n'Play layout is not supported, exit 1), and so does main.

Suspect code

  • crates/socket-patch-core/src/constants.rs:77: PNP_MARKERS includes .pnp.js.
  • crates/socket-patch-core/src/crawlers/pkg_managers.rs:105: any marker file means YarnBerryPnP, with no check of nodeLinker in .yarnrc.yml or of node_modules/.yarn-state.yml. Agent apply refuses on this at crates/socket-patch-cli/src/commands/apply.rs:811.
  • crates/socket-patch-core/src/vendor/npm_flavor.rs:166 and crates/socket-patch-core/src/vendor/lock_inventory/view.rs:379: the same marker-only check for vendored mode and the lockfile inventory.

Related, but the inverse case: #539 (the vendored PnP refusal is missing on a lock-only PnP checkout). Both come from deciding "PnP" by marker file alone instead of from the configured linker.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions