Skip to content

Registry troubleshooting

401 Unauthorized — even though I’m logged in

Section titled “401 Unauthorized — even though I’m logged in”

Three common causes:

  1. always-auth=true missing from .npmrc — yarn 1 refuses to send the bearer on GET without it. npm/pnpm/bun ignore the flag harmlessly, so just always add it.
  2. Token revoked — check Packages → Manage tokens in the panel. If the row is gone, mint a fresh one.
  3. Wrong host in the auth line — the line must use the exact host serving the registry, e.g. //pier.example.com/registry/npm/:_authToken=…. A trailing or missing slash mismatch causes silent 401.

Yarn berry needs npmAlwaysAuth: true in .yarnrc.yml (not in .npmrc):

npmRegistryServer: "https://YOUR-PIER-HOST/registry/npm/"
npmAuthToken: "pier_npm_…"
npmAlwaysAuth: true
nodeLinker: node-modules

Without it yarn 4 only sends the bearer on npm publish, not on npm install.

You’re re-publishing a version that already exists. Bump the version in package.json (npm version patch / minor / major). Pier rejects re-publishes by design — it matches npm semantics.

If you want to truly replace a published version, npm unpublish @your-org/[email protected] first, then npm publish the new bytes. Beware: external consumers who pinned X.Y.Z and have it in their lockfile will get integrity mismatches.

The packument response carries an ETag. If your client sends If-None-Match with that same value, Pier returns 304. If you see full 200 responses every time, check:

  • Auth header is the same as before. Some HTTP libraries skip If-None-Match when the request changes (different Accept etc.) — make sure your Accept header is stable.
  • Compression — if the response was gzip’d and the client requests it uncompressed (or vice versa), the ETag is unchanged but some intermediaries strip the If-None-Match. Curl with -H "Accept-Encoding: gzip" to repro reliably.

Bun (and recent npm) sends Accept: application/vnd.npm.install-v1+json — Pier returns a leaner JSON (no README, no historical times). If your tooling expects the full packument, drop the abbreviated Accept.

Traefik is up but Pier isn’t reachable on its loopback port. Check systemctl status pier on the host. If pier is up, check that proxy.platform_domain matches the host you’re hitting.

ENOENT / 404 on a public package I just installed

Section titled “ENOENT / 404 on a public package I just installed”

Upstream proxy may not be enabled. Check Packages → Upstream proxy → Enable upstream proxy — when off, only privately-published packages are served.

If proxy is on and you still get 404, the upstream might have actually returned 404. Pier passes that through (a package that genuinely doesn’t exist on npmjs.org should be a 404).

”Tarballs on disk” shows 0 but I just installed

Section titled “”Tarballs on disk” shows 0 but I just installed”

LRU GC may have evicted them. The cache cap is in Packages → Upstream proxy → Max cache size (MiB) — if it’s set to a small value (e.g. 50) and you installed something big like Next.js, the older tarballs get reclaimed.

Set Max cache size to 0 (unlimited) if you have plenty of disk.

You can also re-fetch a specific version from the package detail page — the Download latest (X.Y.Z) button pulls a single tarball through Pier without firing up an npm install.

Detail page shows “0 versions” for a known cached package

Section titled “Detail page shows “0 versions” for a known cached package”

The packument blob may be empty (typically right after a migration that wiped per-version rows). Pier auto-refreshes the blob on detail page load if it’s empty — the first visit might be slow (~1s) while it fetches from upstream. Subsequent visits are instant.

If it stays at 0 after a reload, upstream is either unreachable or returned 404. Check journalctl -u pier | grep proxy.

npm warn Unknown project config "always-auth"

npm 11 deprecated the flag but still respects it. You can ignore the warning. When npm 12 lands and removes it, yarn 1 will be a separate problem (most teams have moved on by then).