jdx/mise · error

blob upload failed

Error message

blob upload failed: {}{}
{}

What it means

upload_blob_once finalized an OCI registry blob upload with a PUT and got a non-success HTTP status; the message embeds the status and the registry's error body. The named OCI registry rejected the upload (auth, digest mismatch, or quota are common).

Solutions

  1. Verify the local blob's digest (`sha256sum <file>`) matches the descriptor in the manifest; re-download/rebuild the blob if not.
  2. Re-authenticate and retry the push.
  3. Retry after transient 5xx; the upload restarts with a fresh session.
  4. Check available disk space / registry quota — some registries reject on quota with 413.
Defensive patterns

Strategy: validation

Validate before calling

let digest =Sha256::digest(&bytes);
let expected = &manifest.layers[i].digest; // "sha256:..."
assert_eq!(format!("sha256:{digest:x}"), *expected, "local blob corrupt; re-pull before push");

Try / catch

if let Err(e) = push_image(dir) {
    let msg = e.to_string();
    if msg.contains("400") { verify_blob_digests(dir); }
    if msg.contains("401") || msg.contains("403") { relogin_and_retry(); }
}

Prevention

When it happens

Trigger: `upload_blob_once` sending the final PUT (with digest query parameter) to close the upload session and receiving e.g. 400 (digest mismatch), 401 (expired token), or 5xx.

Common situations: The assembled blob doesn't match the digest in the PUT's `digest` param (corrupted file on disk or interrupted download into the staging path); credentials expiring during a long upload; registry errors under load.

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/5ac13828f2bb0fd0. Report an issue: GitHub.

Appendix: source

Thrown at src/oci/registry.rs:1436

            let resp = self
                .session
                .send(|auth| {
                    let mut rb = HTTP
                        .reqwest()?
                        .put(put_url.as_str())
                        .header("Content-Length", "0");
                    if let Some(a) = auth {
                        rb = rb.header("Authorization", a);
                    }
                    Ok(rb)
                })
                .await
                .wrap_err("PUT blob upload (finalize)")?;
            let status = resp.status();
            if !status.is_success() {
                resp.error_for_status_ref()?;
                let body = resp.text().await.unwrap_or_default();
                bail!(
                    "blob upload failed: {}{}\n{}",
                    status.as_u16(),
                    push_auth_hint(status, had_credential),
                    body.trim(),
                );
            }
        } else {
            // Monolithic PUT with ?digest=…
            let mut put_url = location;
            put_url.query_pairs_mut().append_pair("digest", digest);
            let err_slot: UploadErrSlot = Default::default();
            let resp = self
                .session
                .send(|auth| {
                    Ok(build_upload_request(
                        HTTP.reqwest()?.put(put_url.as_str()),
                        auth,
                        path,

View on GitHub (pinned to 533346cc37)