jdx/mise · error · eyre::Report

fetching blob {url} failed: {}

Error message

fetching blob {url} failed: {}

What it means

Raised when downloading a blob (image layer) from the registry returns a non-success status that is not transient. Transient statuses (5xx/408/429) are converted by error_for_status_ref into retryable errors handled earlier; this bail! covers deterministic failures such as 401, 403, and 404 on the blob endpoint.

Source

Thrown at src/oci/registry.rs:544

    url: &str,
    pr: Option<&dyn SingleReport>,
) -> Result<Vec<u8>> {
    let resp = session
        .send(|auth| {
            let mut rb = HTTP.reqwest()?.get(url);
            if let Some(a) = auth {
                rb = rb.header("Authorization", a);
            }
            Ok(rb)
        })
        .await
        .wrap_err_with(|| format!("GET {url}"))?;
    let status = resp.status();
    if !status.is_success() {
        // 5xx/408/429 become transient reqwest status errors (retried by the
        // caller); other statuses fall through to a deterministic failure.
        resp.error_for_status_ref()?;
        bail!("fetching blob {url} failed: {}", status.as_u16());
    }
    if let Some(pr) = pr {
        if let Some(len) = resp.content_length() {
            pr.set_length(len);
        }
        pr.set_position(0);
    }
    let mut resp = resp;
    let mut bytes = Vec::new();
    while let Some(chunk) = resp.chunk().await? {
        bytes.extend_from_slice(&chunk);
        if let Some(pr) = pr {
            pr.inc(chunk.len() as u64);
        }
    }
    Ok(bytes)
}

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Re-run the build — if the base manifest is intact, a fresh pull of a different digest/tag may succeed; if the registry is mid-GC, wait for GC to finish
  2. Verify the base image pulls with `docker pull <ref>`; if that also fails with blob errors, the registry's manifest↔blob consistency is broken
  3. Check registry GC logs/schedule and pause GC during heavy pull windows
  4. Re-authenticate (`docker login`) if the status shown is 401/403
Defensive patterns

Strategy: retry

Validate before calling

# Confirm the base image's blobs are intact before the build:
docker pull <base-ref> >/dev/null && echo "registry blobs ok"
# If docker also fails on the blob URL, the registry's manifest↔blob
# consistency is broken (GC race) — fix the registry, not the build.

Try / catch

// Blob fetch failures are often registry-state races; wrap with a retry:
for attempt in 1..=3 {
    match run_mise_oci_build().await {
        Ok(_) => break,
        Err(e) if e.to_string().contains("fetching blob") && attempt < 3 => {
            tokio::time::sleep(Duration::from_secs(2u64.pow(attempt))).await;
        }
        Err(e) => return Err(e),
    }
}

Prevention

When it happens

Trigger: A layer blob referenced by the manifest no longer exists — typically a registry garbage-collection race where the manifest survived but its blobs were collected; credentials expiring mid-pull (401); proxies denying the blob URL (403).

Common situations: Self-hosted registry:2/Harbor/Zot deployments running GC concurrently with pulls; very long CI pulls with short-lived tokens; mirror front-ends that selectively deny blob paths.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/3bb4b8d203fc61fc. Report an issue: GitHub.