{"record":{"id":"68d826414cb6ad40","repo":"jdx/mise","slug":"manifest-push-failed-url","errorCode":null,"errorMessage":"manifest push failed: {} {url}{}\n{}","messagePattern":"manifest push failed: (.+?) (.+?)(.+?)\n(.+?)","errorType":"http","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/oci/registry.rs","lineNumber":1475,"sourceCode":"        let resp = self\n            .session\n            .send(|auth| {\n                let mut rb = HTTP\n                    .reqwest()?\n                    .put(&url)\n                    .header(\"Content-Type\", media_type)\n                    .body(body.clone());\n                if let Some(a) = auth {\n                    rb = rb.header(\"Authorization\", a);\n                }\n                Ok(rb)\n            })\n            .await\n            .wrap_err_with(|| format!(\"PUT {url}\"))?;\n        let status = resp.status();\n        if !status.is_success() {\n            let body = resp.text().await.unwrap_or_default();\n            bail!(\n                \"manifest push failed: {} {url}{}\\n{}\",\n                status.as_u16(),\n                push_auth_hint(status, had_credential),\n                body.trim(),\n            );\n        }\n        Ok(())\n    }\n\n    /// Point `tag` at an OCI image index containing `entry` plus whatever\n    /// other-platform entries the tag already carries. Returns the digest of\n    /// the pushed index.\n    ///\n    /// NOTE: read-modify-write without registry-side concurrency control (the\n    /// Distribution spec has no conditional manifest PUT), so two runners\n    /// updating the same tag at the same instant can race — sequence\n    /// per-platform pushes in CI when that matters.\n    async fn update_tag_index(&mut self, tag: &str, entry: Descriptor) -> Result<String> {","sourceCodeStart":1457,"sourceCodeEnd":1493,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/oci/registry.rs#L1457-L1493","documentation":"The final manifest PUT that tags the pushed image. A non-success status fails here with the HTTP code, auth hint, and the registry's response body. This is where registry-side manifest validation and policy enforcement (MANIFEST_INVALID, DENIED, tag immutability, missing blobs) surface.","triggerScenarios":"Pushing to a tag protected by an immutability policy (409/400 DENIED); a manifest referencing blobs the registry thinks are missing; schema/media-type rejections from older registries; insufficient permission on the tag namespace; subject/referrers constraints.","commonSituations":"Retagging an already-published version protected by an immutable-tag rule (common on Harbor/ECR); mixed-registry pushes where a proxy cached blob state; org policies restricting latest.","solutions":["Read the body line — registries name the exact code (MANIFEST_INVALID, TAG_INVALID, DENIED, BLOB_UNKNOWN) which determines the fix","For immutable-tag rejections: push a new tag/version instead of overwriting","For BLOB_UNKNOWN: re-run the full push so all layers upload before the manifest (or verify a prior partial push did not half-complete)","For permission errors: confirm the credential has push scope on the repository namespace (ghcr.io: write:packages)"],"exampleFix":"# before — overwriting an immutable tag\nmise oci push registry.example.com/acme/app:1.2.3\n\n# after — push a new tag\nmise oci push registry.example.com/acme/app:1.2.4","handlingStrategy":"try-catch","validationCode":"# Before pushing to a protected tag, check the registry's immutability rules\n# and prefer unique tags per build:\nTAG=\"$(git rev-parse --short HEAD)\"\nmise oci push registry.example.com/acme/app:\"$TAG\"   # unique → policy-safe\n# Verify the manifest validates upstream first:\ncrane validate --remote registry.example.com/acme/app:\"$TAG\" 2>/dev/null || true","typeGuard":null,"tryCatchPattern":"// Branch on the registry error code embedded in the body:\nlet msg = String::from_utf8_lossy(&out.stderr);\nif msg.contains(\"manifest push failed\") {\n    let upper = msg.to_uppercase();\n    if upper.contains(\"DENIED\") && msg.contains(\"immutab\") || msg.contains(\"cannot be overwritten\") {\n        // push a new tag instead of overwriting\n    } else if upper.contains(\"MANIFEST_INVALID\") {\n        // schema/media-type issue — check base image manifest type, report to mise\n    } else if upper.contains(\"BLOB_UNKNOWN\") {\n        // re-push so all layers upload before the manifest\n    } else if msg.contains(\"401\") || msg.contains(\"403\") {\n        // fix push scopes / docker login, then retry\n    }\n}","preventionTips":["Push immutable unique tags (git SHA, build number); never reuse released versions","Ensure all blobs finish uploading before the manifest step by letting mise drive the full push (no partial manual uploads)","Read the registry's error code in the message body — it names the exact policy or schema violation"],"tags":["mise","oci","registry","push","manifest","tag-immutability","auth"],"backgroundTag":"container-registry-push-failed","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}