{"record":{"id":"e7fe5d178501c400","repo":"jdx/mise","slug":"base-image-ref-has-layers-in-its-manifest-bu","errorCode":null,"errorMessage":"base image {ref_} has {} layers in its manifest but {} diff_ids in its config — refusing to emit an OCI-spec-violating image","messagePattern":"base image (.+?) has (.+?) layers in its manifest but (.+?) diff_ids in its config — refusing to emit an OCI-spec-violating image","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/oci/builder.rs","lineNumber":271,"sourceCode":"                .get(\"rootfs\")\n                .and_then(|r| r.get(\"diff_ids\"))\n                .and_then(|d| d.as_array())\n                .ok_or_else(|| {\n                    eyre::eyre!(\n                        \"pulled base image {ref_} has no rootfs.diff_ids in its config \\\n                         — cannot produce a valid OCI image on top of it\"\n                    )\n                })?;\n            base_diff_ids = diff_ids_raw\n                .iter()\n                .map(|v| {\n                    v.as_str().map(String::from).ok_or_else(|| {\n                        eyre::eyre!(\"base image {ref_} has a non-string entry in rootfs.diff_ids\")\n                    })\n                })\n                .collect::<Result<Vec<_>>>()?;\n            if base_diff_ids.len() != base_layers.len() {\n                bail!(\n                    \"base image {ref_} has {} layers in its manifest but {} diff_ids in its \\\n                     config — refusing to emit an OCI-spec-violating image\",\n                    base_layers.len(),\n                    base_diff_ids.len()\n                );\n            }\n            platform = pull.platform;\n            base_config_json = Some(pull.config_json);\n        }\n\n        // --- 2. Decide layer reuse and validate tool installs ---\n        // A tool layer is reused from the remote cache image when tool,\n        // version, in-image prefix, and file owner all match — in that case\n        // the layer is never built locally and the tool doesn't need to be\n        // installed at all.\n        let owner_str = format!(\"{}:{}\", owner.uid, owner.gid);\n        let python_relocations: Vec<PythonRelocation> = versions\n            .iter()","sourceCodeStart":253,"sourceCodeEnd":289,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/oci/builder.rs#L253-L289","documentation":"When building on top of a base image, the builder cross-checks the number of layers in the base image's manifest against the number of diff_ids in its config blob. The OCI spec requires a 1:1 correspondence between manifest layers and config rootfs.diff_ids. If they diverge, mise refuses to emit an image that would violate the spec (and likely fail to pull/run).","triggerScenarios":"build (via send) with a base image whose registry manifest lists N layers but whose config blob's rootfs.diff_ids contains M != N entries — typically from a truncated/corrupted pull, a registry serving mismatched manifest/config blobs, or a hand-edited/cached image index.","commonSituations":"Proxy or mirror registries serving inconsistent blobs; corrupted local OCI cache after an interrupted download; base images produced by non-compliant tooling; manifest lists resolved to an incompatible platform's config.","solutions":["Clear the cached base image (remove the cached OCI blobs/index for ref_) and pull it again from the registry.","Verify the base image is well-formed, e.g. `skopeo inspect docker://<ref_>` or `crane manifest <ref_>`, and compare layer/diff_id counts.","Pin the base image to a known-good digest (`ref@sha256:...`) to avoid a registry serving inconsistent data.","If a mirror/proxy is in play, bypass it or fix it so manifest and config blobs come from the same source."],"exampleFix":"// before\nlet base = \"registry.example.com/base:latest\";\n\n// after — pin a verified digest\nlet base = \"registry.example.com/base@sha256:abc123...\";","handlingStrategy":"try-catch","validationCode":"// before building, verify base image integrity with an external tool\n// crane manifest <ref> | jq '.layers | length'\n// crane config <ref> | jq '.rootfs.diff_ids | length'  # must be equal","typeGuard":null,"tryCatchPattern":"match builder.send() {\n    Err(e) if e.to_string().contains(\"diff_ids\") => {\n        // purge cached base image and retry the pull\n        purge_base_cache();\n        retry_build();\n    }\n    r => r?,\n}","preventionTips":["Pin base images by digest instead of mutable tags.","Avoid untrusted mirror/proxy registries for base images.","Re-pull the base image after any interrupted network operation.","Verify base images with skopeo/crane in CI before building."],"tags":["oci","container-image","integrity"],"backgroundTag":"schema-validation-failed","analyzedSha":"533346cc374382b41ec5ff70536252b2e96e725c","analyzedAt":"2026-09-17T13:35:38.149Z","contentChangedAt":"2026-09-17T13:35:38.149Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}