Morganamilo/paru · critical

failed to find cache directory

Error message

failed to find cache directory

What it means

Config::new() calls dirs::cache_dir(); when the XDG cache directory cannot be determined (e.g. XDG_CACHE_HOME unset and no home directory), paru cannot locate its cache directory and bails with 'failed to find cache directory'. This happens at startup before any operation runs, so paru aborts immediately.

Solutions

  1. Set HOME to the user's real home directory before running paru
  2. Set XDG_CACHE_HOME (e.g. export XDG_CACHE_HOME=/home/user/.cache) and ensure it exists
  3. If running under systemd, add Environment=HOME=%h or use the user's session environment
  4. Run paru as the intended user, not as a system account without a home dir

Example fix

# before (systemd unit)
[Service]
ExecStart=/usr/bin/paru -Syu

// after (systemd unit)
[Service]
Environment=HOME=%h
ExecStart=/usr/bin/paru -Syu
Defensive patterns

Strategy: validation

Validate before calling

// before invoking paru programmatically
if std::env::var_os("HOME").is_none() && std::env::var_os("XDG_CACHE_HOME").is_none() {
    eprintln!("HOME or XDG_CACHE_HOME must be set");
}

Prevention

When it happens

Trigger: dirs::cache_dir() returns None when running Config::new() at src/config.rs:581 — e.g. $HOME is unset or invalid, or XDG_CACHE_HOME points at a relative/nonexistent path that the dirs crate rejects.

Common situations: Running paru in a systemd unit / cron job / container with no HOME set; running as a user with a missing home directory; stripped-down Docker images lacking XDG env vars.

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 Morganamilo/paru@9ac3578807 (2026-09-12). Data as JSON: /api/errors/e7077cb2bb240b08. Report an issue: GitHub.

Appendix: source

Thrown at src/config.rs:581

                    {
                        bail!(tr!("section can not be called {}", section));
                    }
                    self.pkgbuild_repos.add_repo(section.to_string());
                }
                Ok(())
            }
            CallbackKind::Directive(_, key, value) => self.parse_directive(key, value),
        };

        let filename = cb.filename.unwrap_or("paru.conf");
        err.map_err(|e| anyhow!("{}:{}: {}", filename, cb.line_number, e))
    }
}

impl Config {
    pub fn new() -> Result<Self> {
        let cache =
            dirs::cache_dir().ok_or_else(|| anyhow!(tr!("failed to find cache directory")))?;
        let cache = cache.join("paru");
        let config =
            dirs::config_dir().ok_or_else(|| anyhow!(tr!("failed to find config directory")))?;
        let config = config.join("paru");
        let state = dirs::state_dir()
            .or_else(dirs::cache_dir)
            .ok_or_else(|| anyhow!(tr!("failed to find state directory")))?;
        let state = state.join("paru");

        let build_dir = cache.join("clone");
        let old_old_devel_path = cache.join("devel.json");
        let old_devel_path = state.join("devel.json");
        let devel_path = state.join("devel.toml");
        let config_path = config.join("paru.conf");

        let old = if old_devel_path.exists() {
            Some(&old_devel_path)
        } else if old_old_devel_path.exists() {

View on GitHub (pinned to 9ac3578807)