What happened
Run 32355381481 (Release → Publish to npm + push version tags, 17.5.0) failed after a full green build:
🦋 changeset v3.0.0
Received an unexpected error for @objectstack/account: (no code)
Exit prior to config file resolving
cause
double-loading config "/home/runner/.config/pnpm/rc" as "global", previously loaded as "user"
🦋 Exited with code 1
Nothing was published; the workspace is untouched. @objectstack/account is just the first package to report — every package fails identically.
Root cause
Three layers stack up, and only the third is new:
pnpm run release exports npm_config_globalconfig=~/.config/pnpm/rc to every child process. pnpm forces this into its rawConfig unconditionally ({ globalconfig: path.join(configDir, 'rc') }).changeset publish v3 detects the pnpm workspace and shells out to pnpm info / pnpm publish per package — where v2 always shelled out to npm. So the publish now runs a nested pnpm underneath pnpm run.- That nested pnpm reads the inherited
npm_config_globalconfig back in, and when it delegates info to npm it hands the npm child bothnpm_config_userconfigandnpm_config_globalconfig pointing at that one file. npm refuses to start when its "user" and "global" config resolve to the same path — it aborts in @npmcli/config before it parses argv, which is why the error arrives with no npm error code and changesets prints (no code).
Upstream: pnpm/pnpm#10914 — "pnpm 10.31.0 breaks nested pnpm invocations under pnpm run", closed as a duplicate of #10926, unfixed on the 10.31.0 line we pin.
Why 2026-08-14's release (run 31781157571) was green on the same pnpm pin: it ran @changesets/cli@^2.31.1, whose npm-only spawn saw userconfig=$HOME/.npmrc and globalconfig=~/.config/pnpm/rc — two different files, no collision. The move to @changesets/cli@^3.0.0 is what routed the publish through a nested pnpm.
Reproduction (pnpm 10.31.0, verbatim)
$ pnpm run repro
Exit prior to config file resolving
cause
double-loading config "/root/.config/pnpm/rc" as "global", previously loaded as "user"
pnpm info react --json on its own (not under pnpm run) succeeds — the nesting is the trigger.
Fix
Drop the inherited value in scripts/release-publish.sh before changeset publish:
unset npm_config_globalconfig NPM_CONFIG_GLOBALCONFIG
Verified against the reproduction above: with the unset, the nested pnpm info returns registry JSON. pnpm recomputes its own global config path from configDir either way, so nothing pnpm needs is lost, and npm falls back to its own default global config while keeping $HOME/.npmrc — where the workflow writes NPM_TOKEN — as the user config. pnpm publish's npm child was already passed only globalconfig, never userconfig, so auth resolution is unchanged.
Two alternatives were tested and rejected: forcing npm_config_userconfig=$HOME/.npmrc does not work (the nested pnpm recomputes and overrides it), and pinning a newer pnpm is a repo-wide package-manager move for a bug with no known fixed version on this line.
Impact
No release can be published until this lands. Nothing was partially published — the run died before the first registry write.
What happened
Run 32355381481 (
Release→ Publish to npm + push version tags, 17.5.0) failed after a full green build:Nothing was published; the workspace is untouched.
@objectstack/accountis just the first package to report — every package fails identically.Root cause
Three layers stack up, and only the third is new:
pnpm run releaseexportsnpm_config_globalconfig=~/.config/pnpm/rcto every child process. pnpm forces this into itsrawConfigunconditionally ({ globalconfig: path.join(configDir, 'rc') }).changeset publishv3 detects the pnpm workspace and shells out topnpm info/pnpm publishper package — where v2 always shelled out tonpm. So the publish now runs a nested pnpm underneathpnpm run.npm_config_globalconfigback in, and when it delegatesinfoto npm it hands the npm child bothnpm_config_userconfigandnpm_config_globalconfigpointing at that one file. npm refuses to start when its "user" and "global" config resolve to the same path — it aborts in@npmcli/configbefore it parses argv, which is why the error arrives with no npm error code and changesets prints(no code).Upstream: pnpm/pnpm#10914 — "pnpm 10.31.0 breaks nested pnpm invocations under
pnpm run", closed as a duplicate of #10926, unfixed on the 10.31.0 line we pin.Why 2026-08-14's release (run 31781157571) was green on the same pnpm pin: it ran
@changesets/cli@^2.31.1, whosenpm-only spawn sawuserconfig=$HOME/.npmrcandglobalconfig=~/.config/pnpm/rc— two different files, no collision. The move to@changesets/cli@^3.0.0is what routed the publish through a nested pnpm.Reproduction (pnpm 10.31.0, verbatim)
pnpm info react --jsonon its own (not underpnpm run) succeeds — the nesting is the trigger.Fix
Drop the inherited value in
scripts/release-publish.shbeforechangeset publish:unset npm_config_globalconfig NPM_CONFIG_GLOBALCONFIGVerified against the reproduction above: with the
unset, the nestedpnpm inforeturns registry JSON. pnpm recomputes its own global config path fromconfigDireither way, so nothing pnpm needs is lost, and npm falls back to its own default global config while keeping$HOME/.npmrc— where the workflow writesNPM_TOKEN— as the user config.pnpm publish's npm child was already passed onlyglobalconfig, neveruserconfig, so auth resolution is unchanged.Two alternatives were tested and rejected: forcing
npm_config_userconfig=$HOME/.npmrcdoes not work (the nested pnpm recomputes and overrides it), and pinning a newer pnpm is a repo-wide package-manager move for a bug with no known fixed version on this line.Impact
No release can be published until this lands. Nothing was partially published — the run died before the first registry write.