{"record":{"id":"398edb3a52fe43d8","repo":"jdx/mise","slug":"fetching-manifest-url-failed","errorCode":null,"errorMessage":"fetching {manifest_url} failed: {}","messagePattern":"fetching (.+?) failed: (.+?)","errorType":"http","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/oci/registry.rs","lineNumber":799,"sourceCode":"                .get(&manifest_url)\n                .header(\"Accept\", index_accept.join(\", \"));\n            if let Some(a) = auth {\n                rb = rb.header(\"Authorization\", a);\n            }\n            Ok(rb)\n        })\n        .await\n        .wrap_err_with(|| format!(\"fetching {manifest_url}\"))?;\n    match resp.status() {\n        StatusCode::OK => {}\n        // No previous image under this ref (404), or the ref exists but the\n        // registry won't serve it as a single manifest with our Accept\n        // headers — both are just \"no cache\".\n        StatusCode::NOT_FOUND => return Ok(None),\n        // An auth failure here (private repo we can't read) is also a cache\n        // miss rather than a push-stopping error.\n        StatusCode::UNAUTHORIZED | StatusCode::FORBIDDEN => return Ok(None),\n        s => bail!(\"fetching {manifest_url} failed: {}\", s.as_u16()),\n    }\n    let mut body: serde_json::Value = resp.json().await?;\n    // An index (multi-arch, e.g. a tag maintained with --update-index):\n    // descend into the entry for the build platform so its layers remain\n    // reusable.\n    if body.get(\"manifests\").map(|m| m.is_array()).unwrap_or(false) {\n        // Match the same canonical identity `upsert_platform_manifest` uses,\n        // so descent and upsert agree on which entry represents this host's\n        // platform (arch/os normalized, arm64 variant filled). The host has\n        // no variant / os.version.\n        let host =\n            platform_identity_parts(std::env::consts::ARCH, std::env::consts::OS, None, None);\n        let digest = body\n            .get(\"manifests\")\n            .and_then(|m| m.as_array())\n            .and_then(|entries| {\n                entries.iter().find(|e| {\n                    let p = e.get(\"platform\");","sourceCodeStart":781,"sourceCodeEnd":817,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/oci/registry.rs#L781-L817","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Confirm the destination is a full OCI distribution-spec registry, not a read-only mirror or a differently-typed repository","Check the image reference format (registry/repo:tag) — a 400 here usually means a malformed reference","Retry after the registry recovers for 5xx causes; verify registry health with `crane manifest <dest-ref>`","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"],"exampleFix":null,"handlingStrategy":"validation","validationCode":"# Verify the destination registry answers manifest GETs per spec before pushing:\ncrane manifest registry.example.com/acme/app:sometag >/dev/null; echo \"status=$?\"\n# 404/401 are fine (no cache); connection errors or 5xx indicate a registry\n# problem that will abort `mise oci push` at the cache-fetch step.","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Validate the destination is a full OCI distribution-spec registry before adopting it","Smoke-test new registries with a `crane copy` of a tiny image before wiring them into builds","Keep the pushed reference well-formed (registry/repo:tag) to avoid 400 responses"],"tags":["mise","oci","registry","manifest","push","cache","network"],"backgroundTag":"container-registry-request-failed","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}