{"record":{"id":"3bb4b8d203fc61fc","repo":"jdx/mise","slug":"fetching-blob-url-failed","errorCode":null,"errorMessage":"fetching blob {url} failed: {}","messagePattern":"fetching blob (.+?) failed: (.+?)","errorType":"http","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/oci/registry.rs","lineNumber":544,"sourceCode":"    url: &str,\n    pr: Option<&dyn SingleReport>,\n) -> Result<Vec<u8>> {\n    let resp = session\n        .send(|auth| {\n            let mut rb = HTTP.reqwest()?.get(url);\n            if let Some(a) = auth {\n                rb = rb.header(\"Authorization\", a);\n            }\n            Ok(rb)\n        })\n        .await\n        .wrap_err_with(|| format!(\"GET {url}\"))?;\n    let status = resp.status();\n    if !status.is_success() {\n        // 5xx/408/429 become transient reqwest status errors (retried by the\n        // caller); other statuses fall through to a deterministic failure.\n        resp.error_for_status_ref()?;\n        bail!(\"fetching blob {url} failed: {}\", status.as_u16());\n    }\n    if let Some(pr) = pr {\n        if let Some(len) = resp.content_length() {\n            pr.set_length(len);\n        }\n        pr.set_position(0);\n    }\n    let mut resp = resp;\n    let mut bytes = Vec::new();\n    while let Some(chunk) = resp.chunk().await? {\n        bytes.extend_from_slice(&chunk);\n        if let Some(pr) = pr {\n            pr.inc(chunk.len() as u64);\n        }\n    }\n    Ok(bytes)\n}\n","sourceCodeStart":526,"sourceCodeEnd":562,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/oci/registry.rs#L526-L562","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","solutions":["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","Verify the base image pulls with `docker pull <ref>`; if that also fails with blob errors, the registry's manifest↔blob consistency is broken","Check registry GC logs/schedule and pause GC during heavy pull windows","Re-authenticate (`docker login`) if the status shown is 401/403"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"# Confirm the base image's blobs are intact before the build:\ndocker pull <base-ref> >/dev/null && echo \"registry blobs ok\"\n# If docker also fails on the blob URL, the registry's manifest↔blob\n# consistency is broken (GC race) — fix the registry, not the build.","typeGuard":null,"tryCatchPattern":"// Blob fetch failures are often registry-state races; wrap with a retry:\nfor attempt in 1..=3 {\n    match run_mise_oci_build().await {\n        Ok(_) => break,\n        Err(e) if e.to_string().contains(\"fetching blob\") && attempt < 3 => {\n            tokio::time::sleep(Duration::from_secs(2u64.pow(attempt))).await;\n        }\n        Err(e) => return Err(e),\n    }\n}","preventionTips":["Do not run registry garbage collection concurrently with CI pulls; schedule GC in idle windows","Pre-pull base images on a warmup step so later builds reuse intact local state","Re-authenticate before long pull-heavy pipelines to avoid mid-pull 401s"],"tags":["mise","oci","registry","blob","download","network","garbage-collection"],"backgroundTag":"container-registry-blob-download","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}