Skip to content

Yarn berry reads the wrong nodeLinker when a ${VAR} in .yarnrc.yml is set from .env.yarn: vendored falsely refuses a node-modules project, and agent misses a live PnP project #1611

Closed

Description

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

Summary

Yarn 4 loads the project's .env.yarn file by default (injectEnvironmentFiles, default [".env.yarn?"]). It uses those variables when it expands ${…} in .yarnrc.yml. socket-patch expands ${…} in nodeLinker from this process's environment only (the #1100 fix). It never reads .env.yarn, so it resolves a different linker from the one yarn uses:

  • nodeLinker: "${L:-pnp}" with .env.yarn L=node-modules: yarn installs node_modules/. scan --mode vendored refuses it as Plug'n'Play (vendor_yarn_berry_unsupported, exit 1).
  • nodeLinker: "${L:-node-modules}" with .env.yarn L=pnp: yarn installs PnP (.pnp.cjs, no node_modules). scan --mode agent exits 0, reporting the vulnerable package as package_not_installed. The yarn_pnp_unsupported warning never fires, so the user sees nothing to patch.

These are the #1100 symptoms, from a variable source that the #1100 fix doesn't cover.

Impact

  • Vendored mode can't be used on a node-modules project that picks its linker through .env.yarn, even though the refusal's own remedy ("set nodeLinker: node-modules") already describes the project.
  • In agent mode, a live PnP project gets a clean exit 0 and a "not installed" skip instead of the documented PnP refusal, so a vulnerable dependency goes unpatched without any warning.

Repro (Linux, Node 22, yarn 4.0.2 and 4.18.1)

mkdir app && cd app
echo '{"name":"app","version":"1.0.0","private":true,"dependencies":{"left-pad":"1.3.0"}}' > package.json
printf 'nodeLinker: "${L:-pnp}"\nenableGlobalCache: false\n' > .yarnrc.yml
echo 'L=node-modules' > .env.yarn
touch yarn.lock && yarn install
yarn config get nodeLinker          # node-modules; node_modules/ exists, no .pnp.cjs
socket-patch scan --mode vendored --json --yes   # (patch API mock serving a left-pad@1.3.0 patch)
# -> partialFailure, exit 1: failed vendor_yarn_berry_unsupported
#    "the configured yarn linker is `nodeLinker: pnp` ..."

# The other direction:
printf 'nodeLinker: "${L:-node-modules}"\nenableGlobalCache: false\n' > .yarnrc.yml
echo 'L=pnp' > .env.yarn
rm -rf node_modules && yarn install   # .pnp.cjs, no node_modules
socket-patch scan --mode agent --json --yes
# -> success, exit 0, skipped package_not_installed, no yarn_pnp_unsupported warning

Controls: when L is exported in the shell instead (L=node-modules socket-patch scan --mode vendored … / L=pnp socket-patch scan --mode agent …), vendored applies (exit 0) and agent warns yarn_pnp_unsupported. That's the #1100 fix working. Hosted mode is unaffected in both directions.

Expected vs actual

Cells

OS yarn rc / .env.yarn mode result
Linux 4.0.2 ${L:-pnp} / L=node-modules vendored fail (false vendor_yarn_berry_unsupported, exit 1), 2/2
Linux 4.18.1 ${L:-pnp} / L=node-modules vendored fail, 2/2
Linux 4.0.2 ${L:-node-modules} / L=pnp agent fail (package_not_installed, no PnP warning, exit 0), 2/2
Linux 4.18.1 ${L:-node-modules} / L=pnp agent fail, 2/2
Linux 4.18.1 same, L exported in the process env vendored / agent pass (control)
Linux 4.18.1 either hosted pass
macOS / Windows — — — not probed (probe branches blocked, see ledger #305)

.env.yarn is a yarn 4 feature, so yarn 2/3 aren't affected. I tested main 657fce2 (= release 5.0.1). This has been the behaviour since the #1100 fix: before that, ${…} wasn't expanded at all.

Suspect code

  • crates/socket-patch-core/src/crawlers/pkg_managers.rs:208 (YarnEnv::var): ${NAME} reads only std::env::var. YarnEnv::current() (:182) builds the env without the project's .env.yarn (or the injectEnvironmentFiles list it names).
  • Other readers of the same expansion that probably share the gap (not verified here): formats/yarn/rc.rs:44 process_setting (used by patch/shared_store.rs:356 for nodeLinker/pnpmStoreFolder), and patch/redirect/upstream/npm.rs:605 (npmRegistryServer / npmScopes for the hosted upstream restore, where a .env.yarn-only ${REG:-https://registry.npmjs.org} would expand silently to the fallback).

Note that yarn applies .env.yarn values only where the process environment doesn't already set the variable. A fix should keep that precedence and still honour injectEnvironmentFiles when it's overridden or emptied.

Activity

  1. mikolalysenko commented on Oct 16, 2026

    @mikolalysenko
    CollaboratorAuthor

    Correction to the last note in the description: I tested the precedence, and on yarn 4.0.2 and 4.18.1 .env.yarn overrides a variable that is already exported. With L=pnp exported in the shell and .env.yarn containing L=node-modules, yarn config get nodeLinker prints node-modules. A fix should therefore let the .env.yarn value win over the process environment (and still honour an overridden or emptied injectEnvironmentFiles). As a result, even the exported-variable "control" in the description resolves differently from yarn whenever .env.yarn sets the same variable.


    Generated by Claude Code

  2. mikolalysenko commented on Oct 16, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Triaged as priority:p1 (yarn berry, npm family). Confirmed on main 657fce2: no code under crates/ reads .env.yarn or injectEnvironmentFiles. YarnEnv::current() (crawlers/pkg_managers.rs) sets vars: None, so YarnEnv::var expands ${NAME} from std::env::var alone. This is a gap the #1100 fix left, not a duplicate of #1100. No open PR covers it. A fix should build the variable map once (process env overlaid by the injectEnvironmentFiles files, with .env.yarn winning, as the follow-up comment above found) and share it with formats/yarn/rc.rs process_setting and the hosted upstream registry reader.


    Generated by Claude Code

  3. mikolalysenko commented on Oct 23, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Claiming this issue (shared root cause: yarn ${VAR} expansion in .yarnrc.yml reads only the process env, never the project's injectEnvironmentFiles / .env.yarn). Branch: agent/fix-yarn-env-file-expansion. Claim-ID: 2026-10-23T00:21:07Z-ee5f31


    Generated by Claude Code

  4. mikolalysenko commented on Oct 23, 2026

    @mikolalysenko
    CollaboratorAuthor

    [agent] Draft PR: #1676


    Generated by Claude Code

  5. added 2 commits that reference this issue on Oct 23, 2026
    d92b117
    1a3bb28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions