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
- 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
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
- 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
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
- fetching {url} failed: {}{hint} {}
- fetching {manifest_url} failed: {}
- blob chunk upload failed: {}{} {}
- blob upload failed: {}{} {}
- starting blob upload failed: {} {}{}
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/3bb4b8d203fc61fc.
Report an issue: GitHub.