jdx/mise · error · eyre::Report

fetching {manifest_url} failed: {}

Error message

fetching {manifest_url} failed: {}

What it means

Before pushing, mise fetches the tag's existing manifest from the destination registry to reuse already-uploaded layers (the push cache). 404, 401, and 403 are deliberately treated as "no cache" and continue; any OTHER non-OK status (e.g. 400, 405, 501, or 5xx that survived retries) aborts the push with this error because the registry's manifest endpoint is behaving unexpectedly.

Source

Thrown at src/oci/registry.rs:799

                .get(&manifest_url)
                .header("Accept", index_accept.join(", "));
            if let Some(a) = auth {
                rb = rb.header("Authorization", a);
            }
            Ok(rb)
        })
        .await
        .wrap_err_with(|| format!("fetching {manifest_url}"))?;
    match resp.status() {
        StatusCode::OK => {}
        // No previous image under this ref (404), or the ref exists but the
        // registry won't serve it as a single manifest with our Accept
        // headers — both are just "no cache".
        StatusCode::NOT_FOUND => return Ok(None),
        // An auth failure here (private repo we can't read) is also a cache
        // miss rather than a push-stopping error.
        StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => return Ok(None),
        s => bail!("fetching {manifest_url} failed: {}", s.as_u16()),
    }
    let mut body: serde_json::Value = resp.json().await?;
    // An index (multi-arch, e.g. a tag maintained with --update-index):
    // descend into the entry for the build platform so its layers remain
    // reusable.
    if body.get("manifests").map(|m| m.is_array()).unwrap_or(false) {
        // Match the same canonical identity `upsert_platform_manifest` uses,
        // so descent and upsert agree on which entry represents this host's
        // platform (arch/os normalized, arm64 variant filled). The host has
        // no variant / os.version.
        let host =
            platform_identity_parts(std::env::consts::ARCH, std::env::consts::OS, None, None);
        let digest = body
            .get("manifests")
            .and_then(|m| m.as_array())
            .and_then(|entries| {
                entries.iter().find(|e| {
                    let p = e.get("platform");

View on GitHub (pinned to 9dcfcaa0dc)

Solutions

  1. Confirm the destination is a full OCI distribution-spec registry, not a read-only mirror or a differently-typed repository
  2. Check the image reference format (registry/repo:tag) — a 400 here usually means a malformed reference
  3. Retry after the registry recovers for 5xx causes; verify registry health with `crane manifest <dest-ref>`
  4. Push once with the cache disabled if the tooling offers it, or to a fresh tag, to test whether only the cached-manifest path is broken
Defensive patterns

Strategy: validation

Validate before calling

# Verify the destination registry answers manifest GETs per spec before pushing:
crane manifest registry.example.com/acme/app:sometag >/dev/null; echo "status=$?"
# 404/401 are fine (no cache); connection errors or 5xx indicate a registry
# problem that will abort `mise oci push` at the cache-fetch step.

Prevention

When it happens

Trigger: The destination registry does not properly implement the OCI distribution-spec manifest endpoint (405/501), rejects the reference format (400), or returns persistent 5xx — e.g. a minimal or misconfigured self-hosted registry, or an internal artifact store fronting a partial registry API.

Common situations: Pushing to niche/self-hosted registries (Nexus, Artifactory repo-type mismatches, custom gates) that answer non-standardly on GET /v2/.../manifests/; upstream outages surfacing after retry exhaustion.

Related errors


AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17). Data as JSON: /api/errors/398edb3a52fe43d8. Report an issue: GitHub.