{"record":{"id":"815781a5f4fcac90","repo":"atuinsh/atuin","slug":"parent-requested-but-we-hit-the-recursion-limit","errorCode":null,"errorMessage":"Parent requested but we hit the recursion limit","messagePattern":"Parent requested but we hit the recursion limit","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"warning","filePath":"crates/atuin-client/src/theme.rs","lineNumber":476,"sourceCode":"            Err(e) => {\n                return Err(Box::new(Error::new(\n                    ErrorKind::InvalidInput,\n                    format!(\n                        \"Failed to deserialize theme: {}\",\n                        if debug {\n                            e.to_string()\n                        } else {\n                            \"set theme debug on for more info\".to_string()\n                        }\n                    ),\n                )));\n            }\n        };\n        let colors: HashMap<Meaning, String> = theme_config.colors;\n        let parent: Option<&Theme> = match theme_config.theme.parent {\n            Some(parent_name) => {\n                if max_depth == 0 {\n                    return Err(Box::new(Error::new(\n                        ErrorKind::InvalidInput,\n                        \"Parent requested but we hit the recursion limit\",\n                    )));\n                }\n                Some(self.load_theme(parent_name.as_str(), Some(max_depth - 1)))\n            }\n            None => Some(self.load_theme(\"default\", Some(max_depth - 1))),\n        };\n\n        if debug && name != theme_config.theme.name {\n            tracing::warn!(\n                \"Your theme config name is not the name of your loaded theme {} != {}\",\n                name,\n                theme_config.theme.name\n            );\n        }\n\n        let theme = Theme::from_foreground_colors(theme_config.theme.name, parent, colors, debug);","sourceCodeStart":458,"sourceCodeEnd":494,"githubUrl":"https://github.com/atuinsh/atuin/blob/202f6ad98ee0da165c35cdb2afbc5b13d6ab81a1/crates/atuin-client/src/theme.rs#L458-L494","documentation":"Thrown by ThemeManager::load_theme_from_config when a theme declares theme.parent but the recursion budget max_depth has hit 0. Depth starts at DEFAULT_MAX_DEPTH (10, theme.rs:12) and each parent hop calls load_theme with max_depth - 1, so any inheritance chain longer than 10, or a cycle (a parent of b, b parent of a), exhausts the budget. It is an io::ErrorKind::InvalidInput; via ThemeManager::load_theme it is caught and falls back to '(none)'.","triggerScenarios":"A theme chain of more than 10 files (child.toml -> ... -> 11th ancestor), or a circular parent reference where two themes name each other as parent. Each cycle iteration burns one depth level until max_depth == 0 at theme.rs:475 and the error returns.","commonSituations":"Users creating layered themes that inherit from one another and accidentally reintroducing an ancestor (a -> b -> c -> a); refactoring a theme family so a parent points back at its own child; excessively deep 'base' theme chains built by hand.","solutions":["Flatten the theme inheritance chain to fewer than 10 hops","Break the cycle: ensure no theme transitively lists itself as a parent (grep parent = in your themes directory and follow the chain)","Set parent only to themes that exist and terminate (the default theme has no parent)","Enable theme.debug to see the warn logs tracing which parent name was being loaded when the limit hit"],"exampleFix":"# before (themes/a.toml -> themes/b.toml -> themes/a.toml)\n# a.toml: parent = \"b\" ; b.toml: parent = \"a\"  => cycle\n\n# after (themes/b.toml)\n[theme]\nname = \"b\"\n# parent = \"a\"   # removed; b now inherits from 'default'\n[colors]\nAlertError = \"red\"","handlingStrategy":"validation","validationCode":"// Walk parent links before loading; reject cycles and over-deep chains\nfn chain_depth(dir: &std::path::Path, name: &str, seen: &mut Vec<String>) -> Option<usize> {\n    if seen.iter().any(|s| s == name) { return None; } // cycle\n    seen.push(name.clone());\n    let text = std::fs::read_to_string(dir.join(format!(\"{name}.toml\")).ok()?;\n    let v: toml::Value = text.parse().ok()?;\n    match v.get(\"theme\").and_then(|t| t.get(\"parent\")).and_then(|p| p.as_str()) {\n        Some(p) => chain_depth(dir, p, seen).map(|d| d + 1),\n        None => Some(0),\n    }\n}\n// require chain_depth(...) <= 10 before calling load_theme","typeGuard":null,"tryCatchPattern":"match manager.load_theme_from_file(name, 10) {\n    Ok(t) => t,\n    Err(e) if e.to_string().contains(\"recursion limit\") => {\n        // inheritance chain too deep or cyclic: fall back to 'default'\n        manager.load_theme(\"default\", None)\n    }\n    Err(e) => { /* propagate or fallback */ manager.load_theme(\"(none)\", None) }\n}","preventionTips":["Keep theme inheritance chains short (<= a few levels, hard limit is 10)","Never let a theme's parent chain revisit a name — check for cycles when authoring layered themes","Prefer composing colors directly over deep inheritance","Enable theme.debug to see which parent load hit the limit"],"tags":["theme","recursion","cycle","config","rust","atuin"],"backgroundTag":"config-inheritance-cycle","analyzedSha":"202f6ad98ee0da165c35cdb2afbc5b13d6ab81a1","analyzedAt":"2026-08-16T19:30:24.731Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}