{"record":{"id":"3f961bb0bbdd7bed","repo":"pnpm/pnpm","slug":"err-pnpm-fetch-response-status","errorCode":"ERR_PNPM_FETCH_${response.status}","errorMessage":"GET ${redactUrlCredentials(request.url)}: ${response.statusText} - ${response.status}","messagePattern":"GET (.+?): (.+?) - (.+?)","errorType":"exception","errorClass":"FetchError","httpStatus":null,"severity":"error","filePath":"pnpm11/fetching/tarball-fetcher/src/remoteTarballFetcher.ts","lineNumber":138,"sourceCode":"      let data: Buffer\n      try {\n        const res = await fetchFromRegistry(url, {\n          authHeaderValue,\n          // Tarballs are already compressed; ask the server not to apply an additional\n          // Content-Encoding so Content-Length matches the body we receive and we don't\n          // waste CPU on round-trip re-compression. See https://github.com/pnpm/pnpm/issues/11506\n          headers: { 'accept-encoding': 'identity' },\n          // The fetch library can retry requests on bad HTTP responses.\n          // However, it is not enough to retry on bad HTTP responses only.\n          // Requests should also be retried when the tarball's integrity check fails.\n          // Hence, we tell fetch to not retry,\n          // and we perform the retries from this function instead.\n          retry: { retries: 0 },\n          timeout: gotOpts.timeout,\n        })\n\n        if (res.status !== 200) {\n          throw new FetchError({ url, authHeaderValue }, res)\n        }\n\n        // When Content-Encoding is present, Content-Length refers to the encoded form\n        // of the data, not the decoded bytes that the fetch implementation yields.\n        // See: https://developer.mozilla.org/en-US/docs/Web/HTTP/Reference/Headers/Content-Encoding\n        const isEncoded = isContentEncoded(res.headers.get('content-encoding'))\n        const contentLength = !isEncoded && res.headers.has('content-length') && res.headers.get('content-length')\n        const parsedLength = typeof contentLength === 'string' ? parseInt(contentLength, 10) : NaN\n        const size = Number.isFinite(parsedLength) && parsedLength >= 0 ? parsedLength : null\n        if (opts.onStart != null) {\n          opts.onStart(size, currentAttempt)\n        }\n        // In order to reduce the amount of logs, we only report the download progress of big tarballs\n        const onProgress = (size != null && size >= BIG_TARBALL_SIZE && opts.onProgress)\n          ? throttle(opts.onProgress, 500)\n          : undefined\n        const startTime = Date.now()\n        let downloaded = 0","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/pnpm/pnpm/blob/6261b7f388016d57ca6b90340342411cd1d0d00f/pnpm11/fetching/tarball-fetcher/src/remoteTarballFetcher.ts#L120-L156","documentation":"A remote tarball request returned a non-200 status; the response is wrapped in a FetchError whose code embeds the HTTP status (ERR_PNPM_FETCH_401, ERR_PNPM_FETCH_404, ERR_PNPM_FETCH_429, ...) and whose message shows the credential-redacted URL, statusText, and status. The downloader retries transient failures twice (factor-10 backoff, 10s min / 60s max) but fails fast on 401/403/404 — those are deterministic.","triggerScenarios":"GET of a tarball URL returns non-200: unpublished package or wrong version (404), missing/invalid registry token (401/403), rate limiting (429), registry or proxy outage (5xx), corporate proxy interception.","commonSituations":"Missing .npmrc auth token for private registries; package unpublished or version typo; publish-propagation delay (404 right after publish); npm rate limits in CI; proxy returning 502/503.","solutions":["Decode the embedded status: 401/403 means fix registry auth (set the token in .npmrc for that registry host)","404 means verify the package/version exists (npm view pkg versions) and that the registry URL in .npmrc is correct","429/5xx are transient and already retried — check the registry status page or your proxy, then retry after backoff","If a corporate proxy is in play, bypass it for the registry host or fix its TLS interception"],"exampleFix":"# before\n$ pnpm install @corp/private-pkg\n# ERR_PNPM_FETCH_404 / ERR_PNPM_FETCH_401\n\n# after: point auth at the right registry\n$ cat .npmrc\n@corp:registry=https://registry.corp.com/\n//registry.corp.com/:_authToken=${NPM_TOKEN}\n$ pnpm install @corp/private-pkg","handlingStrategy":"retry","validationCode":"// Probe auth and existence before a full install of a private package\nimport { execa } from 'execa'\n\nasync function assertTarballFetchable (registry: string, pkgName: string, authHeader?: string): Promise<void> {\n  const res = await fetch(`${registry}/${encodeURIComponent(pkgName).replace('%40', '@')}`, {\n    headers: authHeader ? { authorization: authHeader } : {},\n  })\n  if (res.status === 401 || res.status === 403) throw new Error('registry auth missing/invalid — set the token in .npmrc')\n  if (res.status === 404) throw new Error('package not found on registry — check name/version/registry URL')\n}","typeGuard":null,"tryCatchPattern":"function parseFetchStatus (code: string): number | null {\n  const m = /^ERR_PNPM_FETCH_(\\d+)$/.exec(code)\n  return m ? parseInt(m[1], 10) : null\n}\n\ntry {\n  await downloader(url, opts)\n} catch (err) {\n  const status = parseFetchStatus((err as any).code ?? '')\n  if (status != null && (status === 429 || status >= 500)) {\n    return retryWithBackoff(() => downloader(url, opts), { attempts: 5, factor: 2 })\n  }\n  if (status === 401 || status === 403) throw new Error('fix registry credentials', { cause: err })\n  throw err // 404 and friends are deterministic\n}","preventionTips":["Configure per-registry auth tokens in .npmrc for every private scope before installing","Note that 401/403/404 already skip pnpm's internal retries — fix the cause instead of rerunning","Give CI explicit retry-with-backoff around installs to absorb 429/5xx registry episodes"],"tags":["http","network","registry","auth","fetch"],"backgroundTag":"registry-http-error","analyzedSha":"6261b7f388016d57ca6b90340342411cd1d0d00f","analyzedAt":"2026-08-17T18:30:54.750Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}