{"record":{"id":"7491c5cf1ac4b4a5","repo":"jdx/mise","slug":"mise-oci-cannot-mix-apk-and-apt-entries-in-bootst","errorCode":null,"errorMessage":"mise oci cannot mix apk and apt entries in [bootstrap.packages]; use the package manager matching the selected base image","messagePattern":"mise oci cannot mix apk and apt entries in \\[bootstrap\\.packages\\]; use the package manager matching the selected base image","errorType":"exception","errorClass":"eyre::Report","httpStatus":null,"severity":"error","filePath":"src/oci/packages.rs","lineNumber":199,"sourceCode":"            continue;\n        }\n        match mgr.manager.name() {\n            \"apk\" => apk.extend(mgr.requests.clone()),\n            \"apt\" => apt.extend(mgr.requests.clone()),\n            other => unsupported.push(other.to_string()),\n        }\n    }\n    if !unsupported.is_empty() {\n        unsupported.sort();\n        unsupported.dedup();\n        bail!(\n            \"mise oci currently supports only apt and apk entries in [bootstrap.packages]; \\\n             unsupported manager(s): {}\",\n            unsupported.join(\", \")\n        );\n    }\n    if !apk.is_empty() && !apt.is_empty() {\n        bail!(\n            \"mise oci cannot mix apk and apt entries in [bootstrap.packages]; use the package \\\n             manager matching the selected base image\"\n        );\n    }\n    if !apk.is_empty() {\n        Ok(Some((OciPackageManager::Apk, apk)))\n    } else if !apt.is_empty() {\n        Ok(Some((OciPackageManager::Apt, apt)))\n    } else {\n        Ok(None)\n    }\n}\n\nfn unpack_base_layers(layout: &ImageLayout, layers: &[Descriptor], rootfs: &Path) -> Result<()> {\n    for layer in layers {\n        let path = layout.blob_path(&layer.digest);\n        let reader = open_layer_reader(layer, &path)?;\n        let mut archive = Archive::new(reader);","sourceCodeStart":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/jdx/mise/blob/9dcfcaa0dc8747a2577d3270b69bb9d8313b2807/src/oci/packages.rs#L181-L217","documentation":"collect_package_requests refuses configurations where `[bootstrap.packages]` contains entries for BOTH apk and apt. The system-packages feature produces exactly one layer via one package manager, and no base image can serve both package systems, so mise aborts before unpacking the rootfs rather than attempting a doomed install.","triggerScenarios":"mise.toml has non-empty `[bootstrap.packages.apk]` and `[bootstrap.packages.apt]` tables at the same time (possibly merged from layered config files), then `mise oci build` runs.","commonSituations":"Copy-pasting package examples from both Alpine and Debian docs into one file; merging config from a teammate whose base image differs; gradual base-image migration leaving stale entries from the previous distro.","solutions":["Keep only the table matching the selected base image: delete `[bootstrap.packages.apt]` for an Alpine/Wolfi base, or `[bootstrap.packages.apk]` for a Debian/Ubuntu base","Verify the base image actually in use (`FROM alpine:*` → apk, `FROM debian:*`/`ubuntu:*` → apt) before deciding which table survives","Search all layered mise.tomls — the conflicting table may come from a parent config, not the project file"],"exampleFix":"# before (mise.toml)\n[bootstrap.packages.apk]\ncurl = \"any\"\n[bootstrap.packages.apt]\nripgrep = \"any\"\n\n# after — Alpine base, apk only\n[bootstrap.packages.apk]\ncurl = \"any\"\nripgrep = \"any\"","handlingStrategy":"validation","validationCode":"// Before `mise oci build`, assert the config does not carry both managers.\nfn validate_single_manager(cfg: &toml::Table) -> Result<(), String> {\n    let Some(pkgs) = cfg.get(\"bootstrap\").and_then(|b| b.get(\"packages\")).and_then(|p| p.as_table()) else {\n        return Ok(());\n    };\n    let has_apk = pkgs.get(\"apk\").is_some_and(|v| !v.as_table().unwrap_or(&toml::Table::new()).is_empty());\n    let has_apt = pkgs.get(\"apt\").is_some_and(|v| !v.as_table().unwrap_or(&toml::Table::new()).is_empty());\n    if has_apk && has_apt {\n        return Err(\"[bootstrap.packages] mixes apk and apt; keep only the manager matching the base image\".into());\n    }\n    Ok(())\n}","typeGuard":"fn uses_single_package_manager(cfg: &toml::Table) -> bool {\n    let keys = [\"apk\", \"apt\"];\n    keys.iter().filter(|k| table_non_empty(cfg, k)).count() <= 1\n}","tryCatchPattern":null,"preventionTips":["Decide the base image first, then write exactly one [bootstrap.packages.<manager>] table","When changing base images, convert the package table in the same commit","Lint CI config for simultaneous apk and apt tables"],"tags":["mise","oci","bootstrap-packages","config-validation","package-manager"],"backgroundTag":"config-validation-failed","analyzedSha":"9dcfcaa0dc8747a2577d3270b69bb9d8313b2807","analyzedAt":"2026-08-17T14:28:50.624Z","schemaVersion":2},"datasetVersion":"2026-08-21T13:17:26.733Z"}