Skip to content

nx release publish marks every task failed under pnpm 11 ("could not be extracted") because pnpm 11's native publish writes nothing to stdout #35575

@comp615

Description

@comp615

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:

  1. In any Nx workspace using pnpm, set packageManager: "pnpm@11.0.4" (or run via corepack use pnpm@11).
  2. Run nx release publish --tag latest --dry-run (or call releasePublish({...}) programmatically across multiple projects with maxParallel > 1).
  3. 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:

  1. 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 }).
  2. Use pnpm publish --report-summary: pnpm writes pnpm-publish-summary.json with { publishedPackages: [{ name, version }, ...] }. Nx could read that file instead of parsing stdout.
  3. 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).

Metadata

Metadata

Assignees

Labels

priority: highHigh Priority (important issues which affect many people severely)scope: release

Type

No type
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions