jdx/mise · error
blob chunk upload failed
Error message
blob chunk upload failed: {}{}
{} What it means
Thrown when a chunked PATCH upload of blob data returns a status other than 202 Accepted (or 201 Created, which AWS ECR returns despite the spec). The error carries the status, an auth hint, and the registry's response body.
Solutions
- Re-authenticate (`docker login`) — expired bearer tokens commonly cause mid-upload 401s.
- Retry the push; upload sessions are restarted and the chunk offsets recomputed.
- For 416, verify no proxy is modifying/duplicating requests and Content-Range headers.
- Try a monolithic (single PUT) upload path if the registry's PATCH support is broken.
Defensive patterns
Strategy: retry
Try / catch
match upload_blob(blob) {
Err(e) if e.to_string().contains("401") => { relogin(); retry(1); }
Err(e) if is_transient(&e) => backoff_retry(3),
other => other,
} Prevention
- For large layers, obtain a fresh token right before upload to avoid mid-upload expiry.
- Avoid proxies that rewrite/normalize chunked requests (cause 416).
- Test the registry's PATCH support if not using ECR (which needs the 201 exception the code already handles).
When it happens
Trigger: `upload_blob_once` PATCHing a chunk to the registry's upload session URL and receiving e.g. 416 (wrong chunk range), 401 (session token expired), or 5xx from a flaky registry.
Common situations: Very large layers where mid-upload auth tokens expire; uploading to registries that deviate from the dist-spec beyond the ECR 201 case; network middleboxes corrupting chunk ranges (416); self-hosted registry misconfiguration.
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/dca6eebfed7ed3e6.
Report an issue: GitHub.
Appendix: source
Thrown at src/oci/registry.rs:1405
offset,
len,
pr,
&err_slot,
)
// Content-Range is inclusive on both ends.
.header("Content-Range", format!("{}-{}", offset, offset + len - 1)))
})
.await
.wrap_err("PATCH blob chunk")?;
check_upload_err(&err_slot, path)?;
let status = resp.status();
// Per the OCI dist-spec a chunk PATCH returns 202 Accepted, but
// AWS ECR answers with 201 Created. Accept both, as the
// finalizing PUT below already does.
if status != StatusCode::ACCEPTED && status != StatusCode::CREATED {
resp.error_for_status_ref()?;
let body = resp.text().await.unwrap_or_default();
bail!(
"blob chunk upload failed: {}{}\n{}",
status.as_u16(),
push_auth_hint(status, had_credential),
body.trim(),
);
}
location = self.resolve_location(&resp).unwrap_or(location);
offset += len;
}
// Finalize with ?digest=…
let mut put_url = location;
put_url.query_pairs_mut().append_pair("digest", digest);
let resp = self
.session
.send(|auth| {
let mut rb = HTTP
.reqwest()?
.put(put_url.as_str())View on GitHub (pinned to 533346cc37)