{"record":{"id":"4860404b6875327e","repo":"jdx/mise","slug":"fetching-url-failed-hint","errorCode":null,"errorMessage":"fetching {url} failed: {}{hint}\n{}","messagePattern":"fetching (.+?) failed: (.+?)(.+?)\n(.+?)","errorType":"http","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/oci/registry.rs","lineNumber":465,"sourceCode":"                rb = rb.header(\"Authorization\", a);\n            }\n            Ok(rb)\n        })\n        .await\n        .wrap_err_with(|| format!(\"fetching {url}\"))?;\n    let status = resp.status();\n    if !status.is_success() {\n        let hint = if status == StatusCode::UNAUTHORIZED || status == StatusCode::FORBIDDEN {\n            if session.has_credential() {\n                \" — the stored credentials were rejected or lack access to this image\"\n            } else {\n                \" — the image may be private; run `docker login` (or `podman login`) for this registry\"\n            }\n        } else {\n            \"\"\n        };\n        let body = resp.text().await.unwrap_or_default();\n        bail!(\n            \"fetching {url} failed: {}{hint}\\n{}\",\n            status.as_u16(),\n            body.trim()\n        );\n    }\n    let content_type = header_str(&resp, \"content-type\");\n    let body: serde_json::Value = resp\n        .json()\n        .await\n        .wrap_err_with(|| format!(\"parsing JSON response from {url}\"))?;\n    Ok((body, content_type))\n}\n\n/// Retry a transient-failure-prone operation with mise's standard backoff\n/// schedule. Transient means connect/timeout/body errors and 5xx/408/429\n/// statuses surfaced via `error_for_status`. A macro rather than a generic\n/// fn so the operation expression can reborrow `&mut` state (the\n/// [`AuthSession`]) on every attempt.","sourceCodeStart":447,"sourceCodeEnd":483,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/oci/registry.rs#L447-L483","documentation":"fetch_manifest_json is the generic manifest/index GET used while resolving and pulling base images from an OCI registry. Any non-2xx response that is not a retried transient failure (5xx/408/429 are retried with backoff upstream) lands here: mise prints the HTTP status, an auth hint for 401/403 (credentials rejected vs. `docker login` needed), and the registry's response body.","triggerScenarios":"Pulling a base image that does not exist (404 — typo'd repo/tag, deleted tag), a private image without credentials (401/403 with the docker login hint), stored credentials rejected, or 4xx rejections from proxies and misconfigured self-hosted registries.","commonSituations":"ghcr.io private images on CI without prior `docker login`; upstream tags renamed or removed; rate-limit responses after retries exhausted; corporate proxies returning 403 for registry hosts.","solutions":["For 401/403: run `docker login` (or `podman login`) against the registry named in the URL — for ghcr.io use a PAT with `read:packages`","For 404: verify the exact repository and tag/digest with `crane manifest <ref>` or `docker pull <ref>`; fix the base image reference in mise.toml","Read the body line — registries return structured codes (NAME_UNKNOWN, DENIED, TOOMANYREQUESTS) that pinpoint the cause","For transient-looking failures, re-run the build: mise already retried 5xx/408/429, but registry-side state may recover"],"exampleFix":"# before — private base image, no credentials on CI\nbase_image = \"ghcr.io/acme/private-base:1\"\n\n# after — log in first, then build\ndocker login ghcr.io -u $USER -p $GITHUB_TOKEN\nmise oci build","handlingStrategy":"try-catch","validationCode":"# Verify the base image is pullable with current credentials before building:\ncrane manifest ghcr.io/acme/private-base:1 >/dev/null && echo \"ref+auth ok\"\n# Or with docker: docker pull ghcr.io/acme/private-base:1 >/dev/null && echo ok","typeGuard":null,"tryCatchPattern":"// When invoking mise programmatically, branch on the embedded status code:\nlet msg = String::from_utf8_lossy(&out.stderr);\nif msg.contains(\"fetching\") && msg.contains(\"failed: 401\") {\n    // run `docker login <registry>` then retry the build once\n} else if msg.contains(\"failed: 404\") {\n    // base image ref or tag is wrong — fix config, do not retry\n} else {\n    // 5xx etc. — retry with backoff (mise already retried transient statuses)\n}","preventionTips":["Run `docker login` for every private registry as the first CI step","Pin base images by digest so renamed/deleted tags cannot break builds","Add a preflight `crane manifest <base-ref>` check in CI to fail fast with a clearer signal"],"tags":["mise","oci","registry","http","auth","image-pull","network"],"backgroundTag":"container-registry-request-failed","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}