Current Behavior
When running nx release publish (or invoking the @nx/js:release-publish executor) in a workspace using pnpm 11, every publish task fails with:
The pnpm publish output data could not be extracted. Please report this issue on https://github.com/nrwl/nx
even though the package is actually published successfully to the registry.
When releasePublish is called programmatically with multiple projects and the default nx-bail=true, the first batch of publishes complete on the registry but get marked as failed, then every remaining project in the same invocation is skipped with Tasks not run because their dependencies failed or --nx-bail=true. The end result is a half-published release: tags / changelog / version bumps land in source control but most tarballs never reach the registry. A retry sees "No new version updates were found, nothing to publish" because the tags already exist, so the missing tarballs never get a second chance.
Expected Behavior
nx release publish should recognize a successful pnpm publish --json invocation under pnpm 11, mark the task as successful, and not abort the rest of the release.
GitHub Repo
N/A (private monorepo). Reproduction is fully self-contained below.
Steps to Reproduce
Empirical reproduction of the underlying mismatch (no Nx required):
mkdir /tmp/pnpm-json-repro && cd /tmp/pnpm-json-repro
echo '{"name":"@scope/x","version":"0.0.1"}' > package.json
echo "x" > index.js
# pnpm 10.x (delegates to npm CLI) — produces the JSON Nx expects:
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
End-to-end reproduction inside Nx:
- In any Nx workspace using pnpm, set
packageManager: "pnpm@11.0.4" (or run via corepack use pnpm@11).
- Run
nx release publish --tag latest --dry-run (or call releasePublish({...}) programmatically across multiple projects with maxParallel > 1).
- Observe
"could not be extracted" warnings, success: false for every task in the first parallel batch, and Tasks not run because their dependencies failed or --nx-bail=true for every remaining task.
Root Cause
@nx/js's extract-npm-publish-json-data.ts requires every key of:
const expectedNpmPublishJsonKeys = ['id', 'name', 'version', 'size', 'filename'];
to be present in some JSON object on pnpm publish --json stdout, then release-publish.impl.ts returns success: false if it isn't found.
Up through pnpm 10.x, pnpm publish delegated to the npm CLI, so pnpm publish --json produced an npm-shaped JSON object on stdout (containing id, name, version, size, filename, etc.).
In pnpm 11, pnpm publish is now native (no longer delegates to npm). In the new implementation:
- The
publish handler returns undefined on a successful single-package publish, so main.ts writes nothing to stdout ({ output: null, exitCode: 0 }).
--json only suppresses the human-readable reporter; it does not turn on JSON serialization for publish results.
- The
📦 … / ✅ Published … log lines that would normally print are routed through the reporter that --json disables.
Net result: pnpm publish --json (success) → empty stdout → Nx's regex finds no {...} block → extractNpmPublishJsonData returns { jsonData: null } → success: false.
Suggested Fix
Possible directions:
- Detect package manager and short-circuit for pnpm ≥ 11: if
pm === 'pnpm' and the major is ≥ 11, treat exit code 0 as success without trying to parse stdout (similar to the existing pm === 'bun' branch in release-publish.impl.ts that does console.log(outputStr); return { success: true }).
- Use
pnpm publish --report-summary: pnpm writes pnpm-publish-summary.json with { publishedPackages: [{ name, version }, ...] }. Nx could read that file instead of parsing stdout.
- Pre-pack and verify via
pnpm pack --json: pnpm pack --json does emit { name, version, filename, files: [...] } on stdout; Nx could pack first to capture metadata, then publish the resulting tarball.
Option 1 is the smallest change; option 2 is the most robust and is the documented pnpm-native path for tool integration.
Impact
This silently breaks releases for any monorepo using nx release publish with pnpm 11. The publish step exits non-zero, retries skip the un-published packages (tags already exist), and downstream consumers can't install the bumped versions because the tarballs don't exist on the registry — even though the version bump and tag are committed to the default branch and the release looks "done" in the changelog.
Versions
- nx / @nx/js: 22.7.0
- pnpm: 11.0.4
- node: 24.11.0
- OS: Linux (Ubuntu 24.04) / macOS (reproducible on both)
🤖 Filed with assistance from an AI coding agent (Amp / Claude).
Current Behavior
When running
nx release publish(or invoking the@nx/js:release-publishexecutor) in a workspace using pnpm 11, every publish task fails with:even though the package is actually published successfully to the registry.
When
releasePublishis called programmatically with multiple projects and the defaultnx-bail=true, the first batch of publishes complete on the registry but get marked as failed, then every remaining project in the same invocation is skipped withTasks not run because their dependencies failed or --nx-bail=true. The end result is a half-published release: tags / changelog / version bumps land in source control but most tarballs never reach the registry. A retry sees"No new version updates were found, nothing to publish"because the tags already exist, so the missing tarballs never get a second chance.Expected Behavior
nx release publishshould recognize a successfulpnpm publish --jsoninvocation under pnpm 11, mark the task as successful, and not abort the rest of the release.GitHub Repo
N/A (private monorepo). Reproduction is fully self-contained below.
Steps to Reproduce
Empirical reproduction of the underlying mismatch (no Nx required):
End-to-end reproduction inside Nx:
packageManager: "pnpm@11.0.4"(or run viacorepack use pnpm@11).nx release publish --tag latest --dry-run(or callreleasePublish({...})programmatically across multiple projects withmaxParallel > 1)."could not be extracted"warnings,success: falsefor every task in the first parallel batch, andTasks not run because their dependencies failed or --nx-bail=truefor every remaining task.Root Cause
@nx/js'sextract-npm-publish-json-data.tsrequires every key of:to be present in some JSON object on
pnpm publish --jsonstdout, thenrelease-publish.impl.tsreturnssuccess: falseif it isn't found.Up through pnpm 10.x,
pnpm publishdelegated to thenpmCLI, sopnpm publish --jsonproduced an npm-shaped JSON object on stdout (containingid,name,version,size,filename, etc.).In pnpm 11,
pnpm publishis now native (no longer delegates to npm). In the new implementation:publishhandler returnsundefinedon a successful single-package publish, somain.tswrites nothing to stdout ({ output: null, exitCode: 0 }).--jsononly suppresses the human-readable reporter; it does not turn on JSON serialization for publish results.📦 …/✅ Published …log lines that would normally print are routed through the reporter that--jsondisables.Net result:
pnpm publish --json(success) → empty stdout → Nx's regex finds no{...}block →extractNpmPublishJsonDatareturns{ jsonData: null }→success: false.Suggested Fix
Possible directions:
pm === 'pnpm'and the major is ≥ 11, treat exit code 0 as success without trying to parse stdout (similar to the existingpm === 'bun'branch inrelease-publish.impl.tsthat doesconsole.log(outputStr); return { success: true }).pnpm publish --report-summary: pnpm writespnpm-publish-summary.jsonwith{ publishedPackages: [{ name, version }, ...] }. Nx could read that file instead of parsing stdout.pnpm pack --json:pnpm pack --jsondoes emit{ name, version, filename, files: [...] }on stdout; Nx could pack first to capture metadata, then publish the resulting tarball.Option 1 is the smallest change; option 2 is the most robust and is the documented pnpm-native path for tool integration.
Impact
This silently breaks releases for any monorepo using
nx release publishwith pnpm 11. The publish step exits non-zero, retries skip the un-published packages (tags already exist), and downstream consumers can't install the bumped versions because the tarballs don't exist on the registry — even though the version bump and tag are committed to the default branch and the release looks "done" in the changelog.Versions
🤖 Filed with assistance from an AI coding agent (Amp / Claude).