{"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":317,"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":299,"sourceCodeEnd":335,"githubUrl":"https://github.com/jdx/mise/blob/533346cc374382b41ec5ff70536252b2e96e725c/src/system/resources.rs#L299-L335","documentation":"During bootstrap planning, the provisioner topologically sorts declared resources by their dependencies. If the sort yields fewer resources than were declared, a dependency cycle exists among the resources listed in the message. The library refuses to order (and therefore provision) a plan it can never execute.","triggerScenarios":"Calling the bootstrap plan/ordered ordering code (reached via plan -> output) when two or more declared resources (directories, files, users, groups, compose projects) depend on each other, directly or transitively, e.g. directory A depends on B and B depends on A.","commonSituations":"Hand-written resource manifests where a directory declares a parent that itself declares a dependency back on the child; copy-pasted resource blocks with swapped dependency directions; a compose project and a directory each listing the other in explicit_dependencies.","solutions":["Inspect the resource IDs listed in the error message and find the mutual/transitive dependency among them","Remove or reverse one dependency edge so the graph becomes a DAG","Split the cyclic group into independently ordered resources with a one-directional dependency","Ensure explicit_dependencies only reference resources that cannot reference back"],"exampleFix":"// before\ndir_a.depends_on(\"dir:b\");\ndir_b.depends_on(\"dir:a\");\n// after\ndir_b.depends_on(\"dir:a\"); // one direction only","handlingStrategy":"validation","validationCode":"const ids = new Set(resources.map(r => r.id));\nfor (const r of resources) for (const d of r.depends_on) if (ids.has(d)) checkCycle(resources, r.id, d); // DFS for back-edges before calling the planner","typeGuard":"function isAcyclic(res) { const seen = new Set(), stack = new Set(); function visit(id) { if (stack.has(id)) return false; if (seen.has(id)) return true; stack.add(id); const deps = res.find(r => r.id === id)?.depends_on ?? []; for (const d of deps) if (ids.has(d) && !visit(d)) return false; stack.delete(id); seen.add(id); return true; } return res.every(r => visit(r.id)); }","tryCatchPattern":null,"preventionTips":["Keep dependencies one-directional: lower-level resources (users, groups, dirs) never depend on higher-level ones (files, compose projects)","Validate the dependency graph for cycles in CI before deploying bootstrap configs","Derive dependencies from structure (parent dirs) rather than listing them manually"],"tags":["bootstrap","dependency-cycle","provisioning"],"backgroundTag":"invalid-state-transition","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"}