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
- 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
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
- 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
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
- blob chunk upload failed: {}{} {}
- blob upload failed: {}{} {}
- manifest push failed: {} {url}{} {}
- --cache-from must reference the same repository as the desti
- fetching {url} failed: {}{hint} {}
AI-assisted analysis of jdx/mise@9dcfcaa0dc (2026-08-17).
Data as JSON: /api/errors/398edb3a52fe43d8.
Report an issue: GitHub.