atuinsh/atuin · warning · std::io::Error
Empty theme directory override and could not find theme else
Error message
Empty theme directory override and could not find theme elsewhere
What it means
Thrown by ThemeManager::load_theme_from_file when the theme-directory override (the theme_dir argument to ThemeManager::new, or the ATUIN_THEME_DIR environment variable) is present but an empty string. With an override set, the manager never falls back to the default config directory, so an empty value leaves nowhere to look for {name}.toml and it returns io::ErrorKind::NotFound immediately. Note that the higher-level ThemeManager::load_theme catches this and falls back to the built-in '(none)' theme with a tracing::warn, so end users see it as a log line, not a crash.
Source
Thrown at crates/atuin-client/src/theme.rs:416
loaded_themes: HashMap::new(),
debug: debug.unwrap_or(false),
override_theme_dir: match theme_dir {
Some(theme_dir) => Some(theme_dir),
None => std::env::var("ATUIN_THEME_DIR").ok(),
},
}
}
// Try to load a theme from a `{name}.toml` file in the theme directory. If an override is set
// for the theme dir (via ATUIN_THEME_DIR env) we should load the theme from there
pub fn load_theme_from_file(
&mut self,
name: &str,
max_depth: u8,
) -> Result<&Theme, Box<dyn error::Error>> {
let mut theme_file = if let Some(p) = &self.override_theme_dir {
if p.is_empty() {
return Err(Box::new(Error::new(
ErrorKind::NotFound,
"Empty theme directory override and could not find theme elsewhere",
)));
}
PathBuf::from(p)
} else {
let config_dir = atuin_common::utils::config_dir();
let mut theme_file = if let Ok(p) = std::env::var("ATUIN_CONFIG_DIR") {
PathBuf::from(p)
} else {
let mut theme_file = PathBuf::new();
theme_file.push(config_dir);
theme_file
};
theme_file.push("themes");
theme_file
};
View on GitHub (pinned to 202f6ad98e)
Solutions
- Unset ATUIN_THEME_DIR entirely (`unset ATUIN_THEME_DIR`) so the manager uses the config directory themes folder
- Set ATUIN_THEME_DIR to an existing directory that contains a {theme-name}.toml file
- Pass None as the theme_dir argument instead of Some("") when constructing ThemeManager programmatically
- Use a built-in theme name (default, autumn, marine) which never touches the filesystem
Example fix
# before export ATUIN_THEME_DIR="" # after (option A: remove it) unset ATUIN_THEME_DIR # after (option B: point it somewhere real) export ATUIN_THEME_DIR="$HOME/.config/atuin/themes"
Defensive patterns
Strategy: validation
Validate before calling
// Before constructing the manager, reject/normalize an empty override
let theme_dir = std::env::var("ATUIN_THEME_DIR").ok();
let theme_dir = theme_dir.filter(|s| !s.trim().is_empty()); // None => default dir
let manager = ThemeManager::new(Some(debug), theme_dir); Try / catch
match manager.load_theme_from_file(name, 10) {
Ok(theme) => { /* use theme */ }
Err(e) if e.kind() == std::io::ErrorKind::NotFound => {
// empty override or missing file: fall back to a builtin
let theme = manager.load_theme("default", None);
}
Err(e) => { /* surface */ }
} Prevention
- Never export ATUIN_THEME_DIR to an empty string; unset it instead
- In wrapper scripts use [ -n "$THEME_DIR" ] && export ATUIN_THEME_DIR="$THEME_DIR"
- Treat empty theme_dir overrides as None when calling ThemeManager::new programmatically
- Remember ThemeManager::load_theme already degrades to the '(none)' builtin, so log capture is usually enough
When it happens
Trigger: Calling ThemeManager::new(None, Some("".to_string())) or running with ATUIN_THEME_DIR exported as an empty string, then requesting any theme that is not a built-in (default, (none), autumn, marine). The check at theme.rs:415 fires before any filesystem access, so the named theme file never even matters.
Common situations: A wrapper script or CI environment that exports ATUIN_THEME_DIR="" (e.g. `ENV_VAR=${VAR:-}` patterns); dotfiles managers that template the variable to blank when unset; test fixtures that deliberately pass an empty override (the crate's own tests do this via ThemeManager::new(Some(false), Some("".to_string()))).
Related errors
- Failed to deserialize theme: {}
- Parent requested but we hit the recursion limit
- key file vanished immediately after a concurrent write
- failed to create client
- Failed to compute stats
AI-assisted analysis of atuinsh/atuin@202f6ad98e (2026-08-16).
Data as JSON: /api/errors/8979e16f11f50306.
Report an issue: GitHub.