{"record":{"id":"8979e16f11f50306","repo":"atuinsh/atuin","slug":"empty-theme-directory-override-and-could-not-find","errorCode":null,"errorMessage":"Empty theme directory override and could not find theme elsewhere","messagePattern":"Empty theme directory override and could not find theme elsewhere","errorType":"validation","errorClass":"std::io::Error","httpStatus":null,"severity":"warning","filePath":"crates/atuin-client/src/theme.rs","lineNumber":416,"sourceCode":"            loaded_themes: HashMap::new(),\n            debug: debug.unwrap_or(false),\n            override_theme_dir: match theme_dir {\n                Some(theme_dir) => Some(theme_dir),\n                None => std::env::var(\"ATUIN_THEME_DIR\").ok(),\n            },\n        }\n    }\n\n    // Try to load a theme from a `{name}.toml` file in the theme directory. If an override is set\n    // for the theme dir (via ATUIN_THEME_DIR env) we should load the theme from there\n    pub fn load_theme_from_file(\n        &mut self,\n        name: &str,\n        max_depth: u8,\n    ) -> Result<&Theme, Box<dyn error::Error>> {\n        let mut theme_file = if let Some(p) = &self.override_theme_dir {\n            if p.is_empty() {\n                return Err(Box::new(Error::new(\n                    ErrorKind::NotFound,\n                    \"Empty theme directory override and could not find theme elsewhere\",\n                )));\n            }\n            PathBuf::from(p)\n        } else {\n            let config_dir = atuin_common::utils::config_dir();\n            let mut theme_file = if let Ok(p) = std::env::var(\"ATUIN_CONFIG_DIR\") {\n                PathBuf::from(p)\n            } else {\n                let mut theme_file = PathBuf::new();\n                theme_file.push(config_dir);\n                theme_file\n            };\n            theme_file.push(\"themes\");\n            theme_file\n        };\n","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/atuinsh/atuin/blob/202f6ad98ee0da165c35cdb2afbc5b13d6ab81a1/crates/atuin-client/src/theme.rs#L398-L434","documentation":"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.","triggerScenarios":"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.","commonSituations":"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()))).","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"],"exampleFix":"# before\nexport ATUIN_THEME_DIR=\"\"\n# after (option A: remove it)\nunset ATUIN_THEME_DIR\n# after (option B: point it somewhere real)\nexport ATUIN_THEME_DIR=\"$HOME/.config/atuin/themes\"","handlingStrategy":"validation","validationCode":"// Before constructing the manager, reject/normalize an empty override\nlet theme_dir = std::env::var(\"ATUIN_THEME_DIR\").ok();\nlet theme_dir = theme_dir.filter(|s| !s.trim().is_empty()); // None => default dir\nlet manager = ThemeManager::new(Some(debug), theme_dir);","typeGuard":null,"tryCatchPattern":"match manager.load_theme_from_file(name, 10) {\n    Ok(theme) => { /* use theme */ }\n    Err(e) if e.kind() == std::io::ErrorKind::NotFound => {\n        // empty override or missing file: fall back to a builtin\n        let theme = manager.load_theme(\"default\", None);\n    }\n    Err(e) => { /* surface */ }\n}","preventionTips":["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"],"tags":["theme","config","env-var","filesystem","rust","atuin"],"backgroundTag":"empty-env-var","analyzedSha":"202f6ad98ee0da165c35cdb2afbc5b13d6ab81a1","analyzedAt":"2026-08-16T19:30:24.731Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}