Last pnpm version that worked
10.x (any version that delegated to the npm CLI for publish)
pnpm version
11.0.4
Code to reproduce the issue
Empirical reproduction of the regression (no other tooling required):
mkdir /tmp/pnpm-publish-json-repro && cd /tmp/pnpm-publish-json-repro
echo '{"name":"@scope/x","version":"0.0.1"}' > package.json
echo "x" > index.js
# pnpm 10.x (delegates to npm CLI) — produces JSON on stdout:
npx pnpm@10 publish --json --dry-run --no-git-checks --registry=https://example.invalid
# stdout:
# {
# "id": "@scope/x@0.0.1",
# "name": "@scope/x",
# "version": "0.0.1",
# "size": 211,
# "filename": "scope-x-0.0.1.tgz",
# ...
# }
# pnpm 11.x (native publish) — outputs literally 0 bytes on stdout:
npx pnpm@11 publish --json --dry-run --no-git-checks --registry=https://example.invalid
# stdout: (empty, 0 bytes)
# exit code: 0
Expected behavior
pnpm publish --json should emit machine-readable JSON describing the published package(s) on stdout, consistent with the documented --json flag and with pnpm 10.x behavior. The flag is still listed in pnpm publish --help ("Show information in JSON format"), so consumers reasonably expect output.
Actual behavior
pnpm publish --json produces zero bytes on stdout when publish succeeds (exit code 0). The flag is silently a no-op for pnpm publish in v11.
Root cause
In pnpm 10 and earlier, pnpm publish shelled out to the npm CLI, so --json was forwarded to npm and npm produced an npm-shaped JSON object on stdout (containing id, name, version, size, filename, files, etc.).
PR #10591 ("feat!: replace npm publish with libnpmpublish") replaced that passthrough with an in-process libnpmpublish call. In the new implementation:
- The
publish handler in releasing/commands/src/publish/publish.ts returns { exitCode?, manifest?, publishedManifest? } on success — never a string — so pnpm/src/main.ts writes nothing to stdout.
--json is declared in the publish command's cliOptionsTypes (json: Boolean) and documented in --help, but opts.json is never read in the publish handler.
- The reporter that prints the human-readable
📦 … / Published … lines is correctly suppressed by the existing --json handling at pnpm/src/main.ts:184 (const printLogs = !config['parseable'] && !config['json']), so JSON would not be polluted by log output if the handler emitted any.
Net result: pnpm publish --json (success) → empty stdout → downstream tools that parse the JSON break.
Impact
This silently breaks any tooling that parses pnpm publish --json output. Concrete cases:
Precedent
The directly analogous regression pnpm -g ls --json (#11440) was treated and fixed as a regression in PR #11451 (merged 2026-05-04), with new tests asserting valid JSON output. The same expectation applies here: --json is a documented flag and consumers — including pnpm's own ecosystem (Nx, changesets-style flows) — depend on it producing parseable JSON.
Node.js version
24.11.0
Operating System
Last pnpm version that worked
10.x (any version that delegated to the
npmCLI for publish)pnpm version
11.0.4
Code to reproduce the issue
Empirical reproduction of the regression (no other tooling required):
Expected behavior
pnpm publish --jsonshould emit machine-readable JSON describing the published package(s) on stdout, consistent with the documented--jsonflag and with pnpm 10.x behavior. The flag is still listed inpnpm publish --help("Show information in JSON format"), so consumers reasonably expect output.Actual behavior
pnpm publish --jsonproduces zero bytes on stdout when publish succeeds (exit code 0). The flag is silently a no-op forpnpm publishin v11.Root cause
In pnpm 10 and earlier,
pnpm publishshelled out to thenpmCLI, so--jsonwas forwarded to npm and npm produced an npm-shaped JSON object on stdout (containingid,name,version,size,filename,files, etc.).PR #10591 ("feat!: replace
npm publishwithlibnpmpublish") replaced that passthrough with an in-processlibnpmpublishcall. In the new implementation:publishhandler inreleasing/commands/src/publish/publish.tsreturns{ exitCode?, manifest?, publishedManifest? }on success — never a string — sopnpm/src/main.tswrites nothing to stdout.--jsonis declared in the publish command'scliOptionsTypes(json: Boolean) and documented in--help, butopts.jsonis never read in the publish handler.📦 …/Published …lines is correctly suppressed by the existing--jsonhandling atpnpm/src/main.ts:184(const printLogs = !config['parseable'] && !config['json']), so JSON would not be polluted by log output if the handler emitted any.Net result:
pnpm publish --json(success) → empty stdout → downstream tools that parse the JSON break.Impact
This silently breaks any tooling that parses
pnpm publish --jsonoutput. Concrete cases:nx release publishtask is marked failed under pnpm 11 (The pnpm publish output data could not be extracted). Withnx-bail=truethe first batch of publishes complete on the registry but the rest of the release is skipped. Tags / changelogs land in source control but most tarballs never reach the registry; retries see "No new version updates were found" because tags already exist. Tracked at nx release publish marks every task failed under pnpm 11 ("could not be extracted") because pnpm 11's native publish writes nothing to stdout nrwl/nx#35575.ncu -g -p pnpmfails parsing pnpm's global metadata under v11 (related issue Bug: ncu -g fails with pnpm v11 due to new global installation structure raineorshine/npm-check-updates#1707; same family of "stdout shape changed" regressions).pnpm publish --json | jq …in CI scripts.Precedent
The directly analogous regression
pnpm -g ls --json(#11440) was treated and fixed as a regression in PR #11451 (merged 2026-05-04), with new tests asserting valid JSON output. The same expectation applies here:--jsonis a documented flag and consumers — including pnpm's own ecosystem (Nx, changesets-style flows) — depend on it producing parseable JSON.Node.js version
24.11.0
Operating System