Skip to content

pnpm publish and pnpm config get resolve scoped registries from different sources, causing silent publishes to the wrong registry #11492

@batusai513

Description

@batusai513

Verify latest release

  • I verified that the issue exists in the latest pnpm release

pnpm version

11.0.6

Which area(s) of pnpm are affected? (leave empty if unsure)

No response

Link to the code that reproduces this issue or a replay of the bug

No response

Reproduction steps

  1. ~/.npmrc:
registry=https://registry.internal.example.com/npm-proxy/
//registry.internal.example.com/:_authToken=${NPM_TOKEN}

@my-org:registry=https://registry.internal.example.com/artifactory/api/npm/npm-releases/
//registry.internal.example.com/artifactory/api/npm/npm-releases/:_authToken=${NPM_TOKEN}
  1. npm-proxy/ is a read-only proxy registry. npm-releases/ is the writable repository where @my-org packages are meant to be published.
  2. pnpm-workspace.yaml at the repo root:
packages:
  - 'packages/*'

registries:
  default: https://registry.internal.example.com/npm-proxy/
  '@my-org': https://registry.internal.example.com/npm-proxy/

dedupePeers: true
lockfileIncludeTarballUrl: true
minimumReleaseAge: 10080
pmOnFail: error
saveExact: true
  1. (The @my-org entry deliberately points at the read-only registry, to make the divergence with .npmrc visible.)
  2. A workspace package at ./packages/my-package with package.json declaring "name": "@my-org/my-package" and listed under packages: in pnpm-workspace.yaml.
  3. From the repo root:
$ pnpm config get @my-org:registry
https://registry.internal.example.com/artifactory/api/npm/npm-releases/

-> pnpm reports the URL from .npmrc.

  1. From the repo root:
$ pnpm publish ./packages/my-package --json --tag=canary --no-git-checks
{
  "error": {
    "code": "E405",
    "message": "405 Method Not Allowed - PUT https://registry.internal.example.com/npm-proxy/@my-org%2fmy-package"
  }
}

-> pnpm publish PUTs to the URL from pnpm-workspace.yaml, not the URL pnpm config get just reported. The target is the read-only proxy, hence the 405.

Describe the Bug

In pnpm v11, a scoped registry key (@:registry) resolves to a different value depending on which command reads it:

  • pnpm config get @:registry returns the value from .npmrc.
  • pnpm publish uses the value from the registries block in pnpm-workspace.yaml.

When the two sources disagree, pnpm publish silently targets the URL from pnpm-workspace.yaml, even though pnpm config get reports a different (and seemingly authoritative) URL. No warning is emitted; the divergence is only visible by inspecting the actual HTTP request the publish makes.

Expected Behavior

  • pnpm config get @:registry and pnpm publish should resolve a scoped registry from the same sources in the same order of precedence — whatever pnpm config get reports should be the URL pnpm publish actually uses.
  • If both .npmrc and pnpm-workspace.yaml's registries block define the same scope with different values, pnpm should emit a clear warning.
  • The documented precedence rule (which source wins) should be stated explicitly in the migration guide.

Actual behavior

  • pnpm config get reads scoped registries from .npmrc.
  • pnpm publish reads scoped registries from pnpm-workspace.yaml's registries block, ignoring the .npmrc value.
  • The two diverge silently. Publishes go to the wrong registry without any indication.

Which Node.js version are you using?

24.15.0

Which operating systems have you used?

  • macOS
  • Windows
  • Linux

If your OS is a Linux based, which one it is? (Include the version if relevant)

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions