atuinsh/atuin · error

could not determine home directory

Error message

could not determine home directory

What it means

home_dir() resolves the user home via directories::BaseDirs::new(), which on Unix reads $HOME. BaseDirs::new() returns None when no home can be determined (HOME unset or empty; on Windows a failed known-folder lookup). Because atuin's config dir, data dir, and database paths all hang off home_dir(), the expect crashes essentially any atuin command.

Source

Thrown at crates/atuin-common/src/utils.rs:107

    if gitdir.parent().is_some() {
        // if .git is a file (worktree), resolve to the main repo root
        if let Some(main_repo) = resolve_git_worktree(&gitdir) {
            return Some(main_repo);
        }
        return Some(gitdir);
    }

    None
}

// TODO: more reliable, more tested
// I don't want to use ProjectDirs, it puts config in awkward places on
// mac. Data too. Seems to be more intended for GUI apps.

pub fn home_dir() -> PathBuf {
    directories::BaseDirs::new()
        .map(|d| d.home_dir().to_path_buf())
        .expect("could not determine home directory")
}

/// Read an environment variable that must be nonempty.
///
/// This function will never return an empty string: if the environment variable is set but empty,
/// [`None`] is returned.
pub fn env_nonempty(name: &str) -> Option<OsString> {
    std::env::var_os(name).filter(|value| !value.is_empty())
}

pub fn config_dir() -> PathBuf {
    let config_dir: PathBuf =
        env_nonempty("XDG_CONFIG_HOME").map_or_else(|| home_dir().join(".config"), Into::into);
    config_dir.join("atuin")
}

pub fn data_dir() -> PathBuf {
    let data_dir: PathBuf = env_nonempty("XDG_DATA_HOME")

View on GitHub (pinned to 202f6ad98e)

Solutions

  1. Set HOME explicitly for the service or container: systemd Environment="HOME=/var/lib/atuin", docker run -e HOME=/home/atuin
  2. Run atuin under a real user account whose passwd entry defines a home (check with getent passwd <user>)
  3. For systemd units prefer User= plus StateDirectory=/HomeDirectory= so the manager provisions the home

Example fix

# before (systemd unit)
[Service]
ExecStart=/usr/bin/atuin daemon

# after
[Service]
User=atuin
Environment=HOME=/var/lib/atuin
StateDirectory=atuin
ExecStart=/usr/bin/atuin daemon
Defensive patterns

Strategy: validation

Validate before calling

if cfg!(unix) && std::env::var_os("HOME").map_or(true, |h| h.is_empty()) {
    eprintln!("HOME is not set; atuin needs it to locate its config and database");
    std::process::exit(2);
}

Try / catch

let home = std::panic::catch_unwind(atuin_common::utils::home_dir)
    .expect("HOME must be set; fix the service environment instead of bypassing");

Prevention

When it happens

Trigger: Any atuin invocation in a process whose environment lacks HOME: systemd services without Environment=, cron jobs, docker run without HOME set, env -i, or a service user with no usable passwd entry.

Common situations: Running the atuin daemon or server as a systemd unit without HOME; Docker images that set USER but not HOME; cron and CI runners; sudo with env_reset.

Related errors


AI-assisted analysis of atuinsh/atuin@202f6ad98e (2026-08-16). Data as JSON: /api/errors/85f59324c8112c8b. Report an issue: GitHub.