rtk-ai/rtk · error

no local data directory available

Error message

no local data directory available

What it means

retriever::db_path builds the on-disk path of the recall SQLite database by starting from the platform's local data directory (dirs::data_local_dir()). On platforms where that directory cannot be determined, it returns this error instead of guessing a path.

Solutions

  1. Set the HOME environment variable to a writable directory (e.g. export HOME=/root in containers).
  2. Set XDG_DATA_HOME to an existing writable path so dirs can resolve the data dir.
  3. Run rtk from an environment with a normal user profile rather than an empty env.

Example fix

// before (Dockerfile)
CMD ["rtk", "gain"]
// after
ENV HOME=/root
CMD ["rtk", "gain"]
Defensive patterns

Strategy: validation

Validate before calling

import os, sys
if not (os.environ.get('HOME') or os.environ.get('XDG_DATA_HOME')):
    sys.exit('Set HOME or XDG_DATA_HOME before running rtk')

Try / catch

match rtk_gain() {
    Err(e) if e.to_string().contains("no local data directory") => {
        std::env::set_var("HOME", "/tmp");
        retry()
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling open/open_existing/with_open when dirs::data_local_dir() returns None — typically when $XDG_DATA_HOME and $HOME are both unset, or on exotic environments (minimal containers, setuid contexts, some CI images) with no resolvable home directory.

Common situations: Running rtk in a Docker container without HOME set; stripped-down CI runners; cron/systemd services with an empty environment; Windows profiles without a LOCALAPPDATA equivalent.

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 rtk-ai/rtk@0924356b4c (2026-09-19). Data as JSON: /api/errors/130a8da4b0798544. Report an issue: GitHub.

Appendix: source

Thrown at src/core/retriever.rs:184

        .read_to_end(&mut out)
        .context("gunzip")?;
    Ok(out)
}

fn db_path(cfg: &RetrieverConfig) -> Result<PathBuf> {
    if let Ok(p) = std::env::var("RTK_RECALL_DB") {
        return Ok(PathBuf::from(p));
    }
    if let Some(ref p) = cfg.database_path {
        return Ok(p.clone());
    }
    // A test that names no store must never reach the developer's own: its rows
    // would ship as real usage through the telemetry ping.
    #[cfg(test)]
    let base = std::env::temp_dir().join(format!("rtk-test-store-{}", std::process::id()));
    #[cfg(not(test))]
    let base = dirs::data_local_dir()
        .ok_or_else(|| anyhow::anyhow!("no local data directory available"))?
        .join(super::constants::RTK_DATA_DIR);
    Ok(base.join(RECALL_DB))
}

fn open(cfg: &RetrieverConfig) -> Result<Connection> {
    let path = db_path(cfg)?;
    if let Some(parent) = path.parent() {
        let _ = crate::core::utils::create_private_dir(parent);
    }
    crate::core::utils::open_private(std::fs::OpenOptions::new().write(true).create(true), &path)
        .with_context(|| format!("pre-create private recall DB: {}", path.display()))?;
    let conn = Connection::open(&path).with_context(|| format!("open {}", path.display()))?;
    // best-effort: NFS / read-only filesystems may reject WAL
    let _ = conn.execute_batch("PRAGMA journal_mode=WAL; PRAGMA busy_timeout=5000;");
    init_schema(&conn)?;
    Ok(conn)
}

View on GitHub (pinned to 0924356b4c)