atuinsh/atuin · error · std::io::Error
Parent requested but we hit the recursion limit
Error message
Parent requested but we hit the recursion limit
What it means
Themes can inherit from a parent theme, and Atuin resolves parents recursively. If the parent chain is deeper than max_depth allows (or the chain contains a cycle), resolution stops and this InvalidInput error is returned rather than recursing forever.
Solutions
- Find and break the cycle: open each theme in the parent chain and remove or fix the `parent` reference that points back to an ancestor.
- Flatten the inheritance: copy the parent's colors into the child theme and drop the `parent` field.
- If the chain is legitimately deep, load with a higher max_depth value.
Example fix
# before (cycle) # mytheme.toml parent = "base" # base.toml also has parent = "mytheme" # after parent = "" # or remove the parent line
Defensive patterns
Strategy: validation
Validate before calling
// detect cycles in the parent chain before loading
fn has_parent_cycle(name: &str, seen: &mut Vec<String>) -> bool {
if seen.contains(&name.to_string()) { return true; }
seen.push(name.to_string());
match parent_of(name) {
Some(p) => has_parent_cycle(&p, seen),
None => false,
}
} Type guard
fn parent_chain_depth_ok(name: &str, limit: u8) -> bool {
let mut cur = Some(name.to_string());
let mut depth: u8 = 0;
while let Some(n) = cur {
depth += 1;
if depth > limit { return false; }
cur = parent_of(&n);
}
true
} Try / catch
match theme_engine.load_theme(name, 3) {
Ok(t) => apply(t),
Err(e) if e.to_string().contains("recursion limit") => {
eprintln!("theme '{name}' parent chain too deep or cyclic; flattening");
apply(flatten_theme(name));
}
Err(e) => return Err(e),
} Prevention
- Never set a theme's parent to itself or to a descendant
- Keep parent chains shallow (<= 2 levels); flatten when possible
- When copying themes between setups, re-check every parent reference exists and acyclic
When it happens
Trigger: load_theme_from_config encounters a theme whose `parent` field is set while max_depth is 0 — caused by a parent chain longer than the allowed depth or a circular parent reference (theme A -> theme B -> theme A).
Common situations: Accidentally circular theme inheritance, very long inheritance chains exceeding the recursion limit passed by the caller, or themes copied from setups that chain multiple custom parents.
Understand the failure class
Background: "Invalid value" and "allowed values are" config errors: what your library rejected and how to fix it — this error's family across 41 libraries.
Related errors
- Empty theme directory override and could not find theme…
- Failed to deserialize theme
- Config key must be non-empty and must not contain whitespace
- Could not build config
- Could not build default
AI-assisted analysis of atuinsh/atuin@900007c480 (2026-09-19).
Data as JSON: /api/errors/815781a5f4fcac90.
Report an issue: GitHub.
Appendix: source
Thrown at crates/atuin-client/src/theme.rs:447
Err(e) => {
return Err(Box::new(Error::new(
ErrorKind::InvalidInput,
format!(
"Failed to deserialize theme: {}",
if debug {
e.to_string()
} else {
"set theme debug on for more info".to_string()
}
),
)));
}
};
let colors: HashMap<Meaning, String> = theme_config.colors;
let parent: Option<&Theme> = match theme_config.theme.parent {
Some(parent_name) => {
if max_depth == 0 {
return Err(Box::new(Error::new(
ErrorKind::InvalidInput,
"Parent requested but we hit the recursion limit",
)));
}
Some(self.load_theme(parent_name.as_str(), Some(max_depth - 1)))
}
None => Some(self.load_theme("default", Some(max_depth - 1))),
};
if debug && name != theme_config.theme.name {
tracing::warn!(
"Your theme config name is not the name of your loaded theme {} != {}",
name,
theme_config.theme.name
);
}
let theme = Theme::from_foreground_colors(theme_config.theme.name, parent, &colors, debug);View on GitHub (pinned to 900007c480)