ultraworkers/claw-code · error · std::io::Error

HOME is not set (on Windows, set USERPROFILE or HOME, or use

Error message

HOME is not set (on Windows, set USERPROFILE or HOME, or use CLAW_CONFIG_HOME to point directly at the config directory)

What it means

Thrown by credentials_home_dir() in the OAuth credentials layer when neither CLAW_CONFIG_HOME nor HOME nor USERPROFILE is set in the process environment. The runtime needs a directory to place ~/.claw/credentials.json for OAuth tokens, and without any home indicator it cannot construct that path. It surfaces as io::Error with ErrorKind::NotFound, so it can masquerade as a file-not-found error if you only match on kind.

Source

Thrown at rust/crates/runtime/src/oauth.rs:340

        error: params.get("error").cloned(),
        error_description: params.get("error_description").cloned(),
    })
}

fn generate_random_token(bytes: usize) -> io::Result<String> {
    let mut buffer = vec![0_u8; bytes];
    File::open("/dev/urandom")?.read_exact(&mut buffer)?;
    Ok(base64url_encode(&buffer))
}

fn credentials_home_dir() -> io::Result<PathBuf> {
    if let Some(path) = std::env::var_os("CLAW_CONFIG_HOME") {
        return Ok(PathBuf::from(path));
    }
    let home = std::env::var_os("HOME")
        .or_else(|| std::env::var_os("USERPROFILE"))
        .ok_or_else(|| {
            io::Error::new(
                io::ErrorKind::NotFound,
                "HOME is not set (on Windows, set USERPROFILE or HOME, \
                 or use CLAW_CONFIG_HOME to point directly at the config directory)",
            )
        })?;
    Ok(PathBuf::from(home).join(".claw"))
}

fn read_credentials_root(path: &PathBuf) -> io::Result<Map<String, Value>> {
    match fs::read_to_string(path) {
        Ok(contents) => {
            if contents.trim().is_empty() {
                return Ok(Map::new());
            }
            serde_json::from_str::<Value>(&contents)
                .map_err(|error| io::Error::new(io::ErrorKind::InvalidData, error))?
                .as_object()
                .cloned()

View on GitHub (pinned to 08106b0c37)

Solutions

  1. Set CLAW_CONFIG_HOME to an explicit config directory (this takes priority and bypasses home-dir lookup entirely): export CLAW_CONFIG_HOME=/path/to/config
  2. Set HOME on Unix: export HOME=/home/user — or USERPROFILE on Windows: set USERPROFILE=C:\Users\user
  3. If spawning claw from code, pass the env var explicitly: std::process::Command::new("claw").env("HOME", home_dir)
  4. For Docker, add ENV HOME=/root or ENV CLAW_CONFIG_HOME=/claw-config to the image
  5. In tests, set CLAW_CONFIG_HOME to a tempfile::tempdir() path instead of mutating HOME (matches the repo's dogfooding convention)

Example fix

// before (fails in container: no HOME, no USERPROFILE)
docker run --rm claw-image claw login

# after
export CLAW_CONFIG_HOME=/tmp/claw-config   # or set HOME=/root in the image
claw login
Defensive patterns

Strategy: validation

Validate before calling

fn ensure_config_home() -> Result<(), std::io::Error> {
    if std::env::var_os("CLAW_CONFIG_HOME").is_some() { return Ok(()); }
    let home = std::env::var_os("HOME")
        .or_else(|| std::env::var_os("USERPROFILE"));
    home.map(|_| ()).ok_or_else(|| std::io::Error::new(
        std::io::ErrorKind::NotFound,
        "set CLAW_CONFIG_HOME, HOME, or USERPROFILE before OAuth operations",
    ))
}

// before any oauth call:
ensure_config_home()?;

Try / catch

match runtime::oauth::load_credentials() {
    Ok(creds) => { /* ... */ }
    Err(e) if e.kind() == std::io::ErrorKind::NotFound
        && e.to_string().contains("HOME is not set") => {
        eprintln!("no home directory: set CLAW_CONFIG_HOME or HOME");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Any call that resolves the credentials path, e.g. the load/save helpers at oauth.rs:266-296 that call credentials_home_dir()?.join("credentials.json") — typically triggered by starting an OAuth login flow or refreshing stored tokens. Occurs when the process is spawned with a scrubbed environment (env -i, minimal Docker containers, systemd units without EnvironmentFile, cron or CI runners that drop HOME).

Common situations: Running the claw binary in Docker/scratch containers that never set HOME; Windows services or scheduled tasks where USERPROFILE is not inherited; su/sudo invocations that reset environment variables; CI pipelines using env -u HOME; test harnesses that clear the environment between cases.

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


AI-assisted analysis of ultraworkers/claw-code@08106b0c37 (2026-08-18). Data as JSON: /api/errors/fc344095cf4d5cc2. Report an issue: GitHub.