jdx/mise · error · eyre::Report

blob upload failed: {}{} {}

Error message

blob upload failed: {}{}
{}

What it means

Final step of the chunked blob upload: a PUT with ?digest=<sha256> to finalize the session. A non-success response fails here with status, auth hint, and body. Digest mismatches (corrupted transfer), expired sessions, or lost permissions at finalize time are the usual statuses.

Source

Thrown at src/oci/registry.rs:1395

            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 9dcfcaa0dc)

Solutions

  1. Re-run the push — a fresh session and re-upload resolves expiry/transient corruption
  2. If DIGEST_INVALID recurs, verify build-machine disk health and reduce parallel memory pressure (corrupt temp files produce wrong digests deterministically)
  3. Reduce layer size or split builds so finalize happens sooner after the last chunk
  4. Check the appended body text for the registry's exact error code
Defensive patterns

Strategy: retry

Validate before calling

# Detect corrupt local blobs before push by re-hashing the largest layers:
sha256sum <image_dir>/blobs/sha256/* | awk '{print $1}' \
  | while read h; do [ -f "<image_dir>/blobs/sha256/$h" ] || echo "missing blob $h"; done
# A blob whose filename digest mismatches its content will deterministically
# fail the finalize PUT — regenerate the layout instead of retrying forever.

Try / catch

// Distinguish deterministic digest failures from transient finalize expiry:
let msg = String::from_utf8_lossy(&out.stderr);
if msg.contains("blob upload failed") {
    if msg.to_uppercase().contains("DIGEST_INVALID") {
        // corruption — re-run `mise oci build`, do NOT retry the push
    } else {
        // session expiry / transient — retry the push once with a fresh session
    }
}

Prevention

When it happens

Trigger: The uploaded bytes do not hash to the expected digest (400 DIGEST_INVALID — corruption in temp files or on the wire); the session Location expired before the finalize PUT; auth dropped between the last chunk and finalize.

Common situations: Flaky networks corrupting large uploads; CI runners with failing scratch disks corrupting the layer blob between build and push; registries with aggressive upload-session timeouts on big layers.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/5ac13828f2bb0fd0. Report an issue: GitHub.