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
- Read the registry body in the error for the exact registry-side reason
- Run the push again with credentials configured (e.g. docker login equivalent / mise credential settings) so the auth hint goes away
- Verify the repository exists and the account has push permission to that namespace
- Check that all layers/blobs referenced by the manifest were pushed before the manifest
- 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
- Configure registry credentials before pushing
- Push all blobs/layers before the manifest
- Keep tag names within registry constraints
- Check repository write permissions for your account
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)