jdx/mise · error

fetching failed

Error message

fetching {url} failed: {}{hint}
{}

What it means

Thrown when an HTTP request to fetch an OCI image manifest from a registry returns a non-success status. The error includes the status code, an auth hint when a private image is suspected, and the trimmed response body so the developer can see the registry's reason.

Solutions

  1. Verify the image URL, repository name, and tag are correct.
  2. Authenticate with the registry: `docker login` or `podman login`, or set the registry credential env/token.
  3. If blocked by rate limits, wait or authenticate to raise the limit.
  4. Inspect the body in the error message — registries often explain the exact rejection there.

Example fix

// before
let manifest = oci.pull_base_image("ghcr.io/acme/priv-img:notatag")?;
// after (verify tag exists and you are logged in)
// $ docker login ghcr.io
// $ docker manifest inspect ghcr.io/acme/priv-img:v1.2.0
let manifest = oci.pull_base_image("ghcr.io/acme/priv-img:v1.2.0")?;
Defensive patterns

Strategy: try-catch

Validate before calling

let status = reqwest::Client::new()
    .head(&url).send().await.map(|r| r.status());
if !matches!(status, Ok(s) if s.is_success() || s == StatusCode::UNAUTHORIZED || s == StatusCode::FORBIDDEN) {
    eprintln!("manifest URL unreachable: {status:?}");
}

Try / catch

match result {
    Err(e) if e.to_string().contains("fetching") => {
        if e.to_string().contains("401") || e.to_string().contains("403") { run_docker_login_hint(); }
        else if e.to_string().contains("429") { backoff_and_retry(); }
        else { return Err(e); }
    }
    r => r,
}

Prevention

When it happens

Trigger: `fetch_manifest_json` receiving a non-2xx response; called from `pull_base_image`, `resolve_manifest`, and `fetch_remote_image` when pulling/building OCI images from a registry URL.

Common situations: Typo'd image tag or repository that doesn't exist (404); private image without `docker login` (401/403); registry rate limits (429 from Docker Hub); corporate proxy blocking the registry.

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/4860404b6875327e. Report an issue: GitHub.

Appendix: source

Thrown at src/oci/registry.rs:472

                rb = rb.header("Authorization", a);
            }
            Ok(rb)
        })
        .await
        .wrap_err_with(|| format!("fetching {url}"))?;
    let status = resp.status();
    if !status.is_success() {
        let hint = if status == StatusCode::UNAUTHORIZED || status == StatusCode::FORBIDDEN {
            if session.has_credential() {
                " — the stored credentials were rejected or lack access to this image"
            } else {
                " — the image may be private; run `docker login` (or `podman login`) for this registry"
            }
        } else {
            ""
        };
        let body = resp.text().await.unwrap_or_default();
        bail!(
            "fetching {url} failed: {}{hint}\n{}",
            status.as_u16(),
            body.trim()
        );
    }
    let content_type = header_str(&resp, "content-type");
    let body: serde_json::Value = resp
        .json()
        .await
        .wrap_err_with(|| format!("parsing JSON response from {url}"))?;
    Ok((body, content_type))
}

/// Retry a transient-failure-prone operation with mise's standard backoff
/// schedule. Transient means connect/timeout/body errors and 5xx/408/429
/// statuses surfaced via `error_for_status`. A macro rather than a generic
/// fn so the operation expression can reborrow `&mut` state (the
/// [`AuthSession`]) on every attempt.

View on GitHub (pinned to 533346cc37)