{"record":{"id":"48e8b1fab11cfb9a","repo":"jdx/mise","slug":"bootstrap-resource-dependency-cycle-cycle","errorCode":null,"errorMessage":"bootstrap resource dependency cycle: {cycle}","messagePattern":"bootstrap resource dependency cycle: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/system/resources.rs","lineNumber":295,"sourceCode":"                for dependent in dependents {\n                    let count = incoming\n                        .get_mut(dependent)\n                        .expect(\"dependent resource is present\");\n                    *count -= 1;\n                    if *count == 0 {\n                        ready.push_back(dependent.clone());\n                    }\n                }\n            }\n        }\n\n        if ordered.len() != self.resources.len() {\n            let cycle = incoming\n                .into_iter()\n                .filter_map(|(id, count)| (count > 0).then_some(id.to_string()))\n                .collect::<Vec<_>>()\n                .join(\", \");\n            bail!(\"bootstrap resource dependency cycle: {cycle}\");\n        }\n        Ok(ordered)\n    }\n}\n\n/// Build the resource plan currently supported by the provisioning engine.\n/// Other bootstrap sections will move into this graph as resource adapters land.\npub(crate) async fn plan(\n    config: &Config,\n    secrets: &super::secrets::SecretValues,\n) -> Result<BootstrapPlan> {\n    let mut plan = BootstrapPlan::default();\n    let accounts = super::accounts::prepare_requests_from_config(config)?;\n    let group_states = accounts\n        .groups\n        .iter()\n        .map(|group| (group.name.clone(), group.state))\n        .collect::<HashMap<_, _>>();","sourceCodeStart":277,"sourceCodeEnd":313,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/system/resources.rs#L277-L313","documentation":"After Kahn's-algorithm ordering, if not all resources were emitted, the remaining ones form a dependency cycle. The error lists the ids still having incoming edges so the developer can see which resources mutually depend on each other.","triggerScenarios":"Calling `ordered` when resources A depends on B and B (directly or transitively) depends on A, so no valid order exists.","commonSituations":"Two directories/files in bootstrap config each declaring the other as parent/dependency; a self-dependency after a config refactor.","solutions":["Read the listed cycle members from the message","Break the cycle by removing or redirecting one depends_on edge (usually the incorrect parent link)","Ensure parent/child relationships form a DAG (parents first, no back-edges)"],"exampleFix":"// before\ndir-a.depends_on = [\"dir-b\"]; dir-b.depends_on = [\"dir-a\"]\n// after\ndir-b.depends_on = [\"dir-a\"]  # drop the back edge","handlingStrategy":"validation","validationCode":"// detect cycles before planning\nfn has_cycle(id: &str, deps: &HashMap<String,Vec<String>>, seen: &mut HashSet<String>) -> bool {\n    if !seen.insert(id.into()) { return true; }\n    deps.get(id).map_or(false, |ds| ds.iter().any(|d| has_cycle(d, deps, seen)))\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Model parent/child relations one-directionally (child -> parent only)","Draw the dependency graph when adding cross-resource links","Never let a resource depend (transitively) on its dependents"],"tags":["bootstrap","dependency-cycle","topological-sort"],"backgroundTag":"invalid-state-transition","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}