{"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":null,"httpStatus":null,"severity":"error","filePath":"src/oci/registry.rs","lineNumber":1436,"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":1418,"sourceCodeEnd":1454,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/oci/registry.rs#L1418-L1454","documentation":"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).","triggerScenarios":"`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.","commonSituations":"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.","solutions":["Verify the local blob's digest (`sha256sum <file>`) matches the descriptor in the manifest; re-download/rebuild the blob if not.","Re-authenticate and retry the push.","Retry after transient 5xx; the upload restarts with a fresh session.","Check available disk space / registry quota — some registries reject on quota with 413."],"exampleFix":null,"handlingStrategy":"validation","validationCode":"let digest =Sha256::digest(&bytes);\nlet expected = &manifest.layers[i].digest; // \"sha256:...\"\nassert_eq!(format!(\"sha256:{digest:x}\"), *expected, \"local blob corrupt; re-pull before push\");","typeGuard":null,"tryCatchPattern":"if let Err(e) = push_image(dir) {\n    let msg = e.to_string();\n    if msg.contains(\"400\") { verify_blob_digests(dir); }\n    if msg.contains(\"401\") || msg.contains(\"403\") { relogin_and_retry(); }\n}","preventionTips":["Verify local blob digests against manifest descriptors before pushing.","Re-authenticate before long uploads; refresh tokens proactively.","Check registry storage quota if blobs are large."],"tags":["network","http","oci","push","blob-upload"],"backgroundTag":"http-error-response","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}