jdx/mise · error

manifest push failed

Error message

manifest push failed: {} {url}{}
{}

What it means

put_manifest fails when the OCI registry responds with a non-success HTTP status to a manifest PUT. The message embeds the status code, an authentication hint computed from whether credentials were supplied, the URL, and the trimmed registry response body so the developer can see the registry's own reason.

Solutions

  1. Read the registry body in the error for the exact registry-side reason
  2. Run the push again with credentials configured (e.g. docker login equivalent / mise credential settings) so the auth hint goes away
  3. Verify the repository exists and the account has push permission to that namespace
  4. Check that all layers/blobs referenced by the manifest were pushed before the manifest
  5. Confirm the manifest format/tag string is accepted by the registry

Example fix

// before
// registry.push_image(...) fails with 401: manifest push failed: 401 https://ghcr.io/v2/org/img/manifests/v1 UNAUTHORIZED
// after
// configure credentials for ghcr.io first (e.g. mise config: [packslip.auth."ghcr.io"] username/token), then retry the push
Defensive patterns

Strategy: try-catch

Validate before calling

// verify credentials exist before pushing
if (!creds.has("ghcr.io")) throw new Error("configure registry credentials before push");

Try / catch

try { await registry.putManifest(...) } catch (e) { if (/manifest push failed: (401|403)/.test(e.message)) { fixCredentials(); } else { throw e; } }

Prevention

When it happens

Trigger: push_image or update_tag_index PUTs an image manifest/index to a registry and the registry returns 4xx/5xx — e.g. 401/403 from missing or expired credentials, 404 because the repository was never created, 422/400 from a malformed manifest, or a blob referenced by the manifest was never pushed.

Common situations: Unauthenticated push (no credential for the registry host), wrong registry URL in config, pushing to a namespace the account cannot write, tag name or manifest schema rejected by the registry, expired token.

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/68d826414cb6ad40. Report an issue: GitHub.

Appendix: source

Thrown at src/oci/registry.rs:1516

        let resp = self
            .session
            .send(|auth| {
                let mut rb = HTTP
                    .reqwest()?
                    .put(&url)
                    .header("Content-Type", media_type)
                    .body(body.clone());
                if let Some(a) = auth {
                    rb = rb.header("Authorization", a);
                }
                Ok(rb)
            })
            .await
            .wrap_err_with(|| format!("PUT {url}"))?;
        let status = resp.status();
        if !status.is_success() {
            let body = resp.text().await.unwrap_or_default();
            bail!(
                "manifest push failed: {} {url}{}\n{}",
                status.as_u16(),
                push_auth_hint(status, had_credential),
                body.trim(),
            );
        }
        Ok(())
    }

    /// Point `tag` at an OCI image index containing `entry` plus whatever
    /// other-platform entries the tag already carries. Returns the digest of
    /// the pushed index.
    ///
    /// NOTE: read-modify-write without registry-side concurrency control (the
    /// Distribution spec has no conditional manifest PUT), so two runners
    /// updating the same tag at the same instant can race — sequence
    /// per-platform pushes in CI when that matters.
    async fn update_tag_index(&mut self, tag: &str, entry: Descriptor) -> Result<String> {

View on GitHub (pinned to 533346cc37)