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

  1. Re-authenticate (`docker login`) — expired bearer tokens commonly cause mid-upload 401s.
  2. Retry the push; upload sessions are restarted and the chunk offsets recomputed.
  3. For 416, verify no proxy is modifying/duplicating requests and Content-Range headers.
  4. 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

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)