sigoden/aichat · critical · Panic

No user's config directory

Error message

No user's config directory

What it means

`config_dir` resolves the application's config directory from (1) an env-var override, (2) `XDG_CONFIG_HOME`, and finally (3) the `dirs` crate's `config_dir()`, which it unwraps with `.expect("No user's config directory")`. On platforms where the OS cannot supply a user config directory (e.g. $HOME unset on Linux, missing profile on Windows), this panics with that message rather than returning an error.

Solutions

  1. Set `XDG_CONFIG_HOME` (or the app's config_dir env override) to a writable directory before running.
  2. Ensure `HOME` is set to an existing, readable directory for the process user.
  3. In containers/services, run as a user that exists in /etc/passwd with a home directory.
  4. Patch the call site to replace `.expect` with graceful fallback (e.g. `./` or temp dir) and a clear error instead of a panic.

Example fix

// before
env::set_var("HOME", ""); // in service environment
// after
// in the service unit or shell:
// Environment=HOME=/home/app  (or XDG_CONFIG_HOME=/etc/app)
// or in code:
if dirs::config_dir().is_none() {
    std::env::set_var("XDG_CONFIG_HOME", "/tmp");
}
Defensive patterns

Strategy: validation

Validate before calling

fn config_env_ready() -> Result<(), String> {
    if std::env::var_os("XDG_CONFIG_HOME").is_some()
        || std::env::var_os("HOME").map(|h| std::path::Path::new(&h).is_dir()).unwrap_or(false)
    {
        Ok(())
    } else {
        Err("set XDG_CONFIG_HOME or a valid HOME; otherwise config_dir() panics".into())
    }
}

Try / catch

// the failure is a panic, not an error — guard before first config access
match std::panic::catch_unwind(|| config::local_path("settings.json")) {
    Ok(p) => load(p),
    Err(_) => {
        eprintln!("No user config directory: set XDG_CONFIG_HOME or HOME");
        std::process::exit(78); // EX_CONFIG
    }
}

Prevention

When it happens

Trigger: Calling `config_dir()`/`local_path()`/any config-loading entry point on a system where `dirs::config_dir()` returns None — typically Linux/Unix with `$HOME` unset or pointing to a nonexistent user, or a stripped container without `/home`.

Common situations: Running the CLI as a systemd service or cron job without HOME set; Docker containers running as a uid with no passwd entry; broken `$HOME` after user migration; Windows roaming-profile failures.

Related errors


AI-assisted analysis of sigoden/aichat@82976d349a (2026-09-09). Data as JSON: /api/errors/6558e322ab27e7af. Report an issue: GitHub.

Appendix: source

Thrown at src/config/mod.rs:295

            config.setup_model()?;
            config.setup_document_loaders();
            config.setup_user_agent();
            Ok(())
        };
        let ret = setup(&mut config);
        if !info_flag {
            ret?;
        }
        Ok(config)
    }

    pub fn config_dir() -> PathBuf {
        if let Ok(v) = env::var(get_env_name("config_dir")) {
            PathBuf::from(v)
        } else if let Ok(v) = env::var("XDG_CONFIG_HOME") {
            PathBuf::from(v).join(env!("CARGO_CRATE_NAME"))
        } else {
            let dir = dirs::config_dir().expect("No user's config directory");
            dir.join(env!("CARGO_CRATE_NAME"))
        }
    }

    pub fn local_path(name: &str) -> PathBuf {
        Self::config_dir().join(name)
    }

    pub fn config_file() -> PathBuf {
        match env::var(get_env_name("config_file")) {
            Ok(value) => PathBuf::from(value),
            Err(_) => Self::local_path(CONFIG_FILE_NAME),
        }
    }

    pub fn roles_dir() -> PathBuf {
        match env::var(get_env_name("roles_dir")) {
            Ok(value) => PathBuf::from(value),

View on GitHub (pinned to 82976d349a)