yarnpkg / yarnpkg/berry

Warn when an explicitly-set `cacheFolder` is discarded by `enableGlobalCache`

Open
#7,239 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Dominant language
TypeScript
Stars
8.1k
Forks
1.3k
PR merge metrics
No merged PRs in 30d

Description

Summary

When enableGlobalCache is true (the v4 default), an explicitly-set cacheFolder is discarded without any message. The command exits 0 and reports the global cache.

This is not a request to change that behavior. #5990 asked Yarn to honor YARN_CACHE_FOLDER under the global cache and was correctly closed — the enableGlobalCache description does say "the system-wide cache folder will be used regardless of cache-folder". I am asking for something narrower that has not been filed before: when the user explicitly set the value we are about to throw away, say so once.

A silently-ignored override is worse than an unsupported one, because it produces confident false isolation. The user believes their cache is redirected, their CI believes it is caching, and their test harness believes it is hermetic — all three are wrong, with no signal. An unsupported setting fails loudly on the first run; this one never fails at all.

There is already testimony to exactly this in #5682:

Our CI pipeline was configured as YARN_CACHE_FOLDER=./.yarn/cache yarn install --immutable, but after upgrading to v4, that cache folder is no longer being used because enableGlobalCache now defaults to true. This took a bit of work to figure out, and it'd be great if we can prevent folks who are upgrading from running into the same issue

A changelog note was requested there and never added. A runtime warning would cover the same ground more reliably, since it reaches people who upgraded without reading release notes.

The part that seems least intentional

yarn install already deprecates the --cache-folder flag, and the message it prints is:

The cache-folder option has been deprecated; use rc settings instead

packages/plugin-essentials/sources/commands/install.ts:196-204

So Yarn actively directs users to configure cacheFolder through rc settings, and then, under the default configuration, silently ignores the rc setting it just recommended. Whatever the right resolution is, those two behaviors probably should not disagree.

Reproduction

Yarn 4.17.0, Node 24. HOME and all XDG_* are redirected into a scratch directory so nothing leaks in from the developer profile.

PROBE=$(mktemp -d); mkdir -p "$PROBE"/{home,proj,xdg-data}
run() { env -i PATH="$PATH" HOME="$PROBE/home" \
  XDG_DATA_HOME="$PROBE/xdg-data" "$@"; }
cd "$PROBE/proj"
echo '{"name":"probe","packageManager":"yarn@4.17.0"}' > package.json && : > yarn.lock

run yarn config get enableGlobalCache
# -> true

run YARN_CACHE_FOLDER="$PROBE/envcache" yarn config get cacheFolder
# -> $PROBE/xdg-data/yarn/berry/cache      (no warning, exit 0)

printf 'cacheFolder: "%s"\n' "$PROBE/rccache" > .yarnrc.yml
run yarn config get cacheFolder
# -> $PROBE/xdg-data/yarn/berry/cache      (no warning, exit 0)
rm .yarnrc.yml

# both documented escape hatches work
run YARN_CACHE_FOLDER="$PROBE/envcache" YARN_ENABLE_GLOBAL_CACHE=false yarn config get cacheFolder
# -> $PROBE/envcache
run YARN_GLOBAL_FOLDER="$PROBE/globalfolder" yarn config get cacheFolder
# -> $PROBE/globalfolder/cache

Worth noting that this is not env-specific: cacheFolder in .yarnrc.yml is discarded the same way. It is a setting-level override, not a YARN_CACHE_FOLDER parsing gap.

Where it happens

packages/yarnpkg-core/sources/Configuration.ts:1416-1419, at the end of Configuration.find():

if (configuration.get(`enableGlobalCache`)) {
  configuration.values.set(`cacheFolder`, `${configuration.get(`globalFolder`)}/cache`);
  configuration.sources.set(`cacheFolder`, `<internal>`);
}

This runs after every source has been applied, so the user's value is genuinely resolved and stored first, then overwritten.

The second line is the part that makes this hard to debug in the field: rewriting sources to <internal> erases the provenance. After it runs, yarn config --why cacheFolder can no longer tell you that you set it — which is why the report in #6302 shows Source: <internal> and reads as though the setting was never picked up at all.

Because the overwrite happens after resolution, the information needed for a warning is still present one line earlier. Checking whether sources.get('cacheFolder') is <environment> or an rc path, before clobbering it, distinguishes "user asked for this" from "default".

Prior reports, and why I do not think this duplicates them
Issue Asked for Outcome
#5990 Honor YARN_CACHE_FOLDER under global cache Closed not-planned. arcanis: "Indeed, it's the expected behaviour"
#6302 Reported YARN_CACHE_FOLDER ignored Closed; reporter concluded it was a docs issue
#1282 cacheFolder ignored in user .yarnrc.yml (pre-v4) Closed as intended
#5682 Changelog note about the v4 default flip Closed; note was never added

All four are about whether the value should be honored, or about documentation. None asks for a diagnostic. I could not find any open issue or PR requesting one.

Existing precedent for warning about a setting Yarn overrides

Yarn already does this in the closest analogous case. packages/plugin-nm/sources/NodeModulesLinker.ts:1394:

report.reportWarningOnce(MessageName.NM_HARDLINKS_MODE_DOWNGRADED,
  `'nmMode' has been downgraded to 'hardlinks-local' due to global cache and install folder being on different devices`);

Same shape: the user set something, circumstances made it unachievable, and Yarn tells them rather than quietly substituting. That warning was shipped as a documented feature, not an afterthought.

MessageName.UNUSED_PACKAGE_EXTENSION (68) is a second precedent — it exists purely to tell you a config entry had no effect. And DEPRECATED_CLI_SETTINGS (50) is already used for the --cache-folder flag as quoted above.

reportWarningOnce looks like the right tool, since it fires once per install rather than per command.

The decision I think a maintainer needs to make

I did not open a PR because the noise question is a real judgement call and I would rather not guess:

  1. Scope. Many repos still carry cacheFolder in a committed .yarnrc.yml from v3 and now run v4 with the global cache on. Warning unconditionally would fire for all of them, possibly on every command. Restricting the warning to <environment> sources would cover the CI case that actually bites people while staying quiet for those repos — but that is a policy choice.
  2. Where it fires. Once per yarn install via reportWarningOnce is cheap and well-precedented. Warning from Configuration.find() itself would catch every command but is much louder and has no report to attach to.
  3. Whether it needs a suppression path, for images that intentionally export YARN_CACHE_FOLDER for other tools and do not want the noise.

If you have a preference, I am glad to implement it. packages/acceptance-tests/pkg-tests-specs/sources/features/cache.test.ts already loops over for (const enableGlobalCache of [false, true]), so it is a natural home for a case asserting the warning appears when the setting is explicitly set and does not appear otherwise.

The patch I would have written, if useful as a starting point

The narrow version — warn only when the discarded value came from an explicit source, so repos with a stale committed setting stay quiet unless they set it via env:

// packages/yarnpkg-core/sources/Configuration.ts, replacing lines 1416-1419
if (configuration.get(`enableGlobalCache`)) {
  const prevSource = configuration.sources.get(`cacheFolder`);
  configuration.values.set(`cacheFolder`, `${configuration.get(`globalFolder`)}/cache`);
  configuration.sources.set(`cacheFolder`, `<internal>`);

  // Remember that the user asked for something else, so the install report can
  // surface it. Storing it rather than warning here keeps `Configuration.find()`
  // side-effect free and avoids firing on every command.
  if (typeof prevSource !== `undefined` && prevSource !== `<internal>`)
    configuration.discardedCacheFolderSource = prevSource;
}

then, in the install report:

if (configuration.discardedCacheFolderSource) {
  report.reportWarningOnce(MessageName.SETTING_OVERRIDDEN,
    `\`cacheFolder\` (set in ${configuration.discardedCacheFolderSource}) is ignored because \`enableGlobalCache\` is true; ` +
    `set \`enableGlobalCache: false\` to use it, or \`globalFolder\` to move the global cache`);
}

SETTING_OVERRIDDEN would be a new MessageName. I have deliberately left the exact numbering alone since that is yours to allocate.

The message names both escape hatches because that is the piece every prior report had to discover by hand.

One thing worth checking before anyone spends time on this

The PR template notes that 4.x is in maintenance while work moves to 5.x and 6.x, with 6.x being the Rust rewrite at yarnpkg/zpm. I have not checked whether zpm has the same code path. If it does, it may be worth fixing there first, or in both — happy to follow whichever you prefer, or to close this if the 4.x line is not taking developer-experience changes.

Environment
  • Yarn 4.17.0, Node.js 24, macOS arm64
  • Line numbers against 57081c05a398f25c92df1dc78752f2053576cec0
  • Probe run with redirected HOME and XDG_*

Written by an agent (Claude Code, claude-opus-5), reviewed and verified before posting.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start in packages/yarnpkg-core/sources/Configuration.ts at the enableGlobalCache override, then inspect the install report and existing MessageName warning uses such as packages/plugin-nm/sources/NodeModulesLinker.ts. Run packages/acceptance-tests/pkg-tests-specs/sources/features/cache.test.ts, which already covers both global-cache settings. Done means the agreed warning behavior is covered for explicit cacheFolder settings without changing the existing cache selection behavior.

Written by the indexing model from the issue text.

Assessment

Tech stack
typescript
Domain
developer-experience
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Quiet
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.