Skip to content
risk confidence: high

npm publish --provenance Failed: the 8 Most Common Reasons and How to Fix Each

The eight failure shapes we see across npm/cli and actions/runner issues when --provenance breaks: OIDC audience drift, Sigstore Fulcio errors, self-hosted runner rejections, and the package.json repository.url mismatch that takes the longest to find.

Published May 14, 2026 · kw: npm publish --provenance failed

Sources: S-001 S-002 S-003 S-004 S-005

npm publish --provenance succeeded all summer, then failed at 2 AM with E_NOATTEST or unable to mint provenance. The eight failure shapes below cover what we have seen in npm/cli issues (S-005) and the GitHub Actions OIDC docs (S-002), with the exact fix for each.

TLDR

The eight most common reasons:

  1. id-token: write is missing or scoped wrong.
  2. registry-url is unset or wrong.
  3. actions/setup-node is older than v4.
  4. NPM_TOKEN is still in the env.
  5. package.json#repository.url does not match the GitHub repo.
  6. Self-hosted runner; npm rejects the OIDC issuer.
  7. Workflow is in a fork, not the upstream repo configured as trusted publisher.
  8. Sigstore Fulcio outage or cert rotation.

1. id-token: write missing or scoped wrong

Symptom: workflow run logs say unable to mint provenance or fall back to legacy publish silently.

Cause: without permissions.id-token: write, GitHub does not issue an OIDC token, so npm’s CLI cannot include provenance.

Fix:

permissions:
  id-token: write
  contents: read

at the workflow or job level. If you use reusable workflows, the inner workflow needs it too — permissions does not auto-propagate.

Source: GitHub OIDC for npm (S-002).

2. registry-url unset or wrong

Symptom: publish succeeds but no provenance badge; the npm CLI says provenance not attested.

Cause: without registry-url, the OIDC audience claim does not point at npm, so the token is technically valid but useless to the registry.

Fix:

- uses: actions/setup-node@v4
  with:
    node-version: 20
    registry-url: "https://registry.npmjs.org"

The URL is literal — https://registry.npmjs.org (no trailing slash, no path). For private registries with their own OIDC support, use their documented URL.

Source: npm trusted-publisher docs (S-001).

3. actions/setup-node older than v4

Symptom: same as #2 — OIDC silently does not engage.

Cause: setup-node v3 wires registry-url for legacy auth but not the OIDC audience flow.

Fix: bump to actions/setup-node@v4 and rerun. Also pin a Node version that ships with npm@9.5 or newer — provenance is not available before that (npm CLI releasesS-003).

4. NPM_TOKEN still in the env

Symptom: publish succeeds, no provenance, no trusted-publisher use, no obvious error in the log.

Cause: npm CLI prefers an explicit _authToken or NODE_AUTH_TOKEN over the OIDC flow. Trusted publishing is silently bypassed.

Fix: delete every reference to NPM_TOKEN, NODE_AUTH_TOKEN, and _authToken from .github/workflows/*, .npmrc, and .npmrc.template. After deleting, rerun a pre-release. The provenance badge must appear.

This is the single most common failure shape in the threads we read on npm/cli (S-005).

5. package.json#repository.url does not match the GitHub repo

Symptom: npm rejects the publish with a trusted-publisher mismatch error, or accepts the publish but emits a warning that the repository URL in the manifest does not match the workflow source.

Cause: npm matches the trusted publisher against the GitHub run identity and the repository.url in the manifest. If repository.url points at a fork or an old org, the check fails.

Fix:

"repository": {
  "type": "git",
  "url": "git+https://github.com/<owner>/<repo>.git"
}

Owner and repo must match exactly what is registered as the trusted publisher on npm.

6. Self-hosted runner; npm rejects the OIDC issuer

Symptom: provenance step fails with unsupported OIDC issuer or audience mismatch.

Cause: GitHub-hosted runners (runs-on: ubuntu-latest) issue OIDC tokens with iss=https://token.actions.githubusercontent.com, which npm trusts. Self-hosted runners may issue tokens that npm currently rejects unless GitHub has wired the same issuer.

Fix: for now, use GitHub-hosted runners for the publish job. You can keep CI on self-hosted and split publishing into a separate job pinned to ubuntu-latest. (GitHub OIDC docsS-002)

7. Workflow is in a fork, not the upstream repo

Symptom: contributors open PRs that try to publish; the workflow runs on the fork, npm rejects the trusted-publisher claim.

Cause: trusted publisher is configured for upstream/repo. PR runs come from fork/repo and the OIDC repository_id claim does not match.

Fix: gate publish on the release or push.tags event from the upstream branch, not on pull_request. PR builds should not attempt to publish in the first place.

on:
  release:
    types: [published]

8. Sigstore Fulcio outage or cert rotation

Symptom: provenance step fails with unable to obtain Fulcio cert or a network error against fulcio.sigstore.dev.

Cause: Sigstore’s Fulcio CA had a transient outage or a cert rotation that broke a pinned chain (Sigstore docsS-004).

Fix: retry. Check the Sigstore status page before assuming your config broke. If retries fail for an hour, fall back to npm publish without --provenance for the urgent release and republish provenance separately when the service is back — but only after confirming the rest of the trusted-publisher flow worked.

Decision tree

SymptomLikely causeFirst check
”unable to mint provenance”id-token permissionWorkflow permissions: block
Publish OK, no badgeNPM_TOKEN present OR registry-url missing.npmrc, workflow env
”audience mismatch”self-hosted runner OR wrong registry-urlruns-on: and registry-url:
”trusted publisher mismatch”repository.url stale OR fork PRpackage.json and trigger event
”Fulcio cert error”Sigstore outagestatus.sigstore.dev

Common mistakes

  • Pinning actions/setup-node@v4.0.0 and forgetting it has known OIDC issues fixed in later v4 patches. Pin to v4 (the major), or pin to a specific SHA after testing.
  • Trying to debug by adding echo $NPM_TOKEN to the workflow. Tokens are masked, but the surrounding env is not always — leak risk.
  • Running the publish locally to “verify the package works” with a personal NPM_TOKEN and then accidentally publishing. Use npm publish --dry-run for local checks.

FAQ

Can I see whether a published version used trusted publishing?

Open the version on npmjs.com. If you see a provenance badge linked to the workflow run + commit, OIDC + provenance worked. If not, it published anyway but bypassed trusted publishing.

How do I retry just the provenance step?

You cannot — provenance is generated during npm publish. To re-attest, you have to publish a new version. There is no separate “attest existing version” CLI flow today.

Does the workflow filename matter?

Yes. The trusted publisher you registered references a specific .github/workflows/<file>.yml. Renaming the file without updating the trusted publisher breaks the check.

Next step

The preflight checklist flags reasons 1, 2, 3, 4, 5, and 7 directly from a paste of your package.json and workflow. Reasons 6 and 8 are runtime conditions — they show up when you publish. Read the OIDC vs NPM_TOKEN migration for the staged switch, and the first-publish setup guide for the clean install.