jdx/mise · error

fetching blob failed

Error message

fetching blob {url} failed: {}

What it means

Thrown when downloading an OCI layer/config blob fails with a non-success HTTP status after retries. Transient statuses (5xx/408/429) are first converted to retryable reqwest errors; any other status reaches this deterministic `bail!` with the status code.

Solutions

  1. Re-pull the image so a fresh manifest referencing existing blobs is fetched.
  2. Check that the blob still exists in the registry (`docker manifest inspect` + registry API GET).
  3. Verify credentials grant read access to the repository's blob storage.
  4. Retry if the status was transient (5xx/408/429) — those are retried automatically, so a persistent failure means a non-transient cause.
Defensive patterns

Strategy: retry

Try / catch

if let Err(e) = download_blob(url) {
    if is_retryable(&e.to_string()) { backoff_retry(3); }
    else if e.to_string().contains("404") { re_pull_manifest_and_retry(); }
    else { return Err(e); }
}

Prevention

When it happens

Trigger: `download_blob_once` receiving a non-success status on the blob GET (e.g. 404 for a blob missing from the registry, 403 for no read permission).

Common situations: A base image manifest references a blob that has been garbage-collected by the registry; blob deleted after manifest fetch (manifest/blob skew); read-protected registry storage; interrupted/failed CDN delivery returning 403.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


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

Appendix: source

Thrown at src/oci/registry.rs:551

    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 533346cc37)