Skip to content
This repository was archived by the owner on May 14, 2026. It is now read-only.
This repository was archived by the owner on May 14, 2026. It is now read-only.

Tarball fetch has no retry on transient network errors #259

@zkochan

Description

@zkochan

Summary

pacquet install aborts the whole install when a single tarball fetch hits a transient network failure (connection reset, TLS error, timeout, DNS blip). pnpm retries; pacquet does not.

Repro

During a large install (1352 packages) I hit:

Error: pacquet_tarball::fetch_tarball

  × installing dependencies
  ├─▶ Failed to fetch https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.1.tgz: error
  │   sending request for url (https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-
  │   1.0.1.tgz)

curl -I on that URL returned HTTP/2 200 immediately afterwards, and rerunning pacquet install --frozen-lockfile completed cleanly — the URL was fine, this was a transient reqwest error.

Where

crates/tarball/src/lib.rs:253-259 — the download path makes a single client.get(package_url).send() call and bubbles any reqwest::Error up as TarballError::FetchTarball:

let response = http_client
    .run_with_permit(|client| client.get(package_url).send())
    .await
    .map_err(network_error)?
    .bytes()
    .await
    .map_err(network_error)?;

No retry wrapper on either the send() or the bytes() step.

What pnpm does

pnpm wraps the entire tarball fetch — body download and addFilesFromTarball (integrity check + extraction) — in one retried closure (fetching/tarball-fetcher/src/remoteTarballFetcher.ts). Only HTTP 401, 403, 404 (and the git-prepare error code) fail fast; every other error — arbitrary 4xx, 5xx, network reset, integrity mismatch, gzip / tar parse error — retries with exponential backoff.

Backoff settings come from fetchRetries, fetchRetryFactor, fetchRetryMintimeout, fetchRetryMaxtimeout (defaults 2, 10, 10000, 60000). pnpm 11 reads them only from pnpm-workspace.yaml (camelCase); they are excluded from NPM_AUTH_SETTINGS (config/config/src/auth.ts), so an .npmrc line is silently ignored.

Proposed fix

Port pnpm's remoteTarballFetcher.ts retry shape:

  • Wrap network + integrity + extract in one retry boundary (so a flaky transfer that survives TCP framing but fails the SHA-512 hash also recovers via re-fetch).
  • Fail fast only on HTTP 401 / 403 / 404; retry everything else.
  • Match pnpm's @zkochan/retry formula: delay = min(min_timeout * factor.pow(attempt), max_timeout), no jitter.
  • Read fetch-retries* from pnpm-workspace.yaml only.

Context: surfaced while testing #258 against a 1352-package lockfile.


Update: Fixed by #301.

Metadata

Metadata

Assignees

Labels

No labels
No labels

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