{"record":{"id":"5ac13828f2bb0fd0","repo":"jdx/mise","slug":"blob-upload-failed","errorCode":null,"errorMessage":"blob upload failed: {}{}\n{}","messagePattern":"blob upload failed: (.+?)(.+?)\n(.+?)","errorType":"http","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/oci/registry.rs","lineNumber":1395,"sourceCode":"            let resp = self\n                .session\n                .send(|auth| {\n                    let mut rb = HTTP\n                        .reqwest()?\n                        .put(put_url.as_str())\n                        .header(\"Content-Length\", \"0\");\n                    if let Some(a) = auth {\n                        rb = rb.header(\"Authorization\", a);\n                    }\n                    Ok(rb)\n                })\n                .await\n                .wrap_err(\"PUT blob upload (finalize)\")?;\n            let status = resp.status();\n            if !status.is_success() {\n                resp.error_for_status_ref()?;\n                let body = resp.text().await.unwrap_or_default();\n                bail!(\n                    \"blob upload failed: {}{}\\n{}\",\n                    status.as_u16(),\n                    push_auth_hint(status, had_credential),\n                    body.trim(),\n                );\n            }\n        } else {\n            // Monolithic PUT with ?digest=…\n            let mut put_url = location;\n            put_url.query_pairs_mut().append_pair(\"digest\", digest);\n            let err_slot: UploadErrSlot = Default::default();\n            let resp = self\n                .session\n                .send(|auth| {\n                    Ok(build_upload_request(\n                        HTTP.reqwest()?.put(put_url.as_str()),\n                        auth,\n                        path,","sourceCodeStart":1377,"sourceCodeEnd":1413,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/oci/registry.rs#L1377-L1413","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Re-run the push — a fresh session and re-upload resolves expiry/transient corruption","If DIGEST_INVALID recurs, verify build-machine disk health and reduce parallel memory pressure (corrupt temp files produce wrong digests deterministically)","Reduce layer size or split builds so finalize happens sooner after the last chunk","Check the appended body text for the registry's exact error code"],"exampleFix":null,"handlingStrategy":"retry","validationCode":"# Detect corrupt local blobs before push by re-hashing the largest layers:\nsha256sum <image_dir>/blobs/sha256/* | awk '{print $1}' \\\n  | while read h; do [ -f \"<image_dir>/blobs/sha256/$h\" ] || echo \"missing blob $h\"; done\n# A blob whose filename digest mismatches its content will deterministically\n# fail the finalize PUT — regenerate the layout instead of retrying forever.","typeGuard":null,"tryCatchPattern":"// Distinguish deterministic digest failures from transient finalize expiry:\nlet msg = String::from_utf8_lossy(&out.stderr);\nif msg.contains(\"blob upload failed\") {\n    if msg.to_uppercase().contains(\"DIGEST_INVALID\") {\n        // corruption — re-run `mise oci build`, do NOT retry the push\n    } else {\n        // session expiry / transient — retry the push once with a fresh session\n    }\n}","preventionTips":["Keep build and push on the same machine/step to avoid artifact corruption in transit","Watch CI runner disk health — corrupted temp files produce wrong digests deterministically","Retry finalize failures once; escalate to rebuild only when DIGEST_INVALID appears"],"tags":["mise","oci","registry","push","upload","digest","network"],"backgroundTag":"container-registry-push-failed","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}