Hmbown/CodeWhale · error
Failed to resolve settings path: no config directory found.
Error message
Failed to resolve settings path: no config directory found.
What it means
`Settings::path()` resolves where settings.toml should live by taking the first available candidate among the primary (~/.codewhale/settings.toml style) home-based path and the legacy config directory. If neither the home directory nor any config directory can be determined (e.g. no HOME/XDG_CONFIG_HOME and no resolvable profile directory), there is nowhere to read or write settings and this error is returned.
Solutions
- Set the HOME environment variable (or USERPROFILE on Windows) to a writable directory before launching
- Set XDG_CONFIG_HOME to an existing writable directory so the primary candidate resolves
- Run the process as a real user account that has a home directory, or pre-create the home dir
Example fix
// before: spawned without HOME
let path = Settings::path()?; // Err: no config directory found
// after: ensure env before launch
std::env::set_var("HOME", "/home/deploy");
std::fs::create_dir_all("/home/deploy/.codewhale")?;
let path = Settings::path()?; Defensive patterns
Strategy: fallback
Validate before calling
let has_home = std::env::var_os("HOME").or_else(|| std::env::var_os("USERPROFILE")).is_some()
|| std::env::var_os("XDG_CONFIG_HOME").is_some();
if !has_home { eprintln!("no config directory resolvable"); } Try / catch
match Settings::path() {
Ok(p) => p,
Err(e) => { eprintln!("set HOME or XDG_CONFIG_HOME: {e}"); std::process::exit(2); }
} Prevention
- Set HOME/USERPROFILE in containers, CI, and service units
- Set XDG_CONFIG_HOME for non-interactive runs
- Pre-create the config directory before first launch
When it happens
Trigger: Calling Settings::path() (public) in an environment where the user's home directory and the system config directory are both unresolvable: HOME unset, XDG_CONFIG_HOME unset, no passwd entry, or a service/CI context without a user profile.
Common situations: Running the TUI in a bare Docker container or systemd service with no HOME set; CI jobs with a stripped environment; cron jobs missing env; running as a user without a home directory.
Understand the failure class
Background: "environment variable is not set" and "Missing keys in environment" errors: what missing required env var messages mean and how to fix them — this error's family across 28 libraries.
Related errors
- CF_ACCOUNT_ID and CF_API_TOKEN are required
- CODEWHALE_HOME / user home is unavailable
- Home directory unavailable
- a CNB access token is not configured in the Codewhale…
- account_agent_model_unconfigured
AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22).
Data as JSON: /api/errors/e4cfa7649711bbef.
Report an issue: GitHub.
Appendix: source
Thrown at crates/tui/src/settings.rs:747
/// `/config preset` command so the bundle is never defined twice.
#[must_use]
pub fn preset_fields(name: &str) -> Option<&'static [(&'static str, &'static str)]> {
match name.trim().to_ascii_lowercase().as_str() {
"calm" => Some(CALM_PRESET_FIELDS),
_ => None,
}
}
impl Settings {
/// Get the canonical settings file path.
///
/// New writes should target `~/.codewhale/settings.toml`. Legacy
/// DeepSeek-branded paths remain readable as fallbacks during load, but we
/// no longer surface them as the primary path in `/config`.
pub fn path() -> Result<PathBuf> {
let (primary, _legacy_home, legacy_config_dir) = settings_path_candidates();
primary.or(legacy_config_dir).ok_or_else(|| {
anyhow::anyhow!("Failed to resolve settings path: no config directory found.")
})
}
/// Load settings from disk, or return defaults if not found
pub fn load() -> Result<Self> {
let mut settings = Self::load_persisted()?;
settings.apply_env_overrides();
Ok(settings)
}
/// Load settings for a diagnostic without migrating a legacy file.
///
/// This preserves the same candidate precedence, parser normalization, and
/// environment overlays as [`Settings::load`]. Unlike an interactive
/// startup, diagnostics must not create `~/.codewhale/settings.toml` just
/// because they inspected a legacy `~/.deepseek/settings.toml` file.
pub(crate) fn load_read_only() -> Result<Self> {
let mut settings = Self::load_persisted_read_only()?;View on GitHub (pinned to 73e0f67d83)