{"record":{"id":"5b74dddf4eb3ab73","repo":"jdx/mise","slug":"cycle-detected-in-tool-overrides","errorCode":null,"errorMessage":"Cycle detected in tool overrides","messagePattern":"Cycle detected in tool overrides","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/toolset/mod.rs","lineNumber":673,"sourceCode":"\n        let mut sorted_ids: Vec<&str> = Vec::with_capacity(graph.node_count());\n        while let Some(Reverse((_, _, id))) = pq.pop() {\n            sorted_ids.push(id);\n\n            for neighbor in graph.neighbors(id) {\n                if let Some(deg) = in_degree.get_mut(neighbor) {\n                    *deg -= 1;\n                    if *deg == 0 {\n                        let p = priorities[neighbor];\n                        let idx = original_index[neighbor];\n                        pq.push(Reverse((p, idx, neighbor)));\n                    }\n                }\n            }\n        }\n\n        if sorted_ids.len() != graph.node_count() {\n            bail!(\"Cycle detected in tool overrides\");\n        }\n\n        let order: HashMap<&str, usize> = sorted_ids\n            .iter()\n            .enumerate()\n            .map(|(i, &id)| (id, i))\n            .collect();\n        installed.sort_by_cached_key(|(b, _)| order.get(b.id()).copied().unwrap_or(usize::MAX));\n\n        Ok(())\n    }\n\n    pub(crate) async fn which(\n        &self,\n        config: &Arc<Config>,\n        bin_name: &str,\n    ) -> Option<(Arc<dyn Backend>, ToolVersion)> {\n        let mut installed = self.list_current_installed_versions(config);","sourceCodeStart":655,"sourceCodeEnd":691,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/toolset/mod.rs#L655-L691","documentation":"sort_by_overrides performs a topological sort of tools based on their override dependencies. If the sort finishes but produced fewer nodes than the dependency graph contains, the graph has a cycle, meaning tool override requirements are mutually contradictory. mise cannot determine a valid install/use order and bails.","triggerScenarios":"Calling sort_by_overrides (Toolset construction/config loading) where tool A's overrides require B and B's require A (directly or transitively), causing the topological sort to emit fewer ids than graph.node_count().","commonSituations":"Misconfigured [tool.overrides] sections in mise.toml where version conditions for two tools each reference the other; nested config files at different directories creating mutual override dependencies.","solutions":["Inspect your mise.toml/.config/mise.toml [tools] overrides and remove the circular dependency between tools","Restructure overrides so dependency direction is one-way (e.g. A depends on B only)","Simplify by pinning one tool's version explicitly instead of conditionally via the other","Use `mise ls` and config files in `mise config ls` to find which files contribute the overrides"],"exampleFix":"// before (cycle)\n[tools.node.overrides]\npython = '3.12'\n[tools.python.overrides]\nnode = '22'\n// after (one-way)\n[tools.python.overrides]\nnode = '22'","handlingStrategy":"validation","validationCode":"// lint config: detect cycles in overrides before running mise\nconst deps = parseOverrides(config); assertNoCycle(deps);","typeGuard":"const acyclic = (graph) => { const seen=new Set(), stack=new Set(); const visit=n=>{ if(stack.has(n)) return false; if(seen.has(n)) return true; stack.add(n); for(const m of graph[n]||[]) if(!visit(m)) return false; stack.delete(n); seen.add(n); return true; }; return [...graph].every(([n])=>visit(n)); };","tryCatchPattern":"try { loadToolset() } catch (e) { if (String(e).includes('Cycle detected in tool overrides')) { reportConfigError(e); } else { throw e; } }","preventionTips":["Keep override dependencies one-directional","Avoid declaring reciprocal overrides across global and project configs","Pin one side explicitly instead of mutually conditioning versions","Review [tools] overrides after adding new config files"],"tags":["config","dependency-cycle","overrides"],"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"}