{"record":{"id":"94e83da49e405c13","repo":"Schniz/fnm","slug":"choosing-base-strategy","errorCode":null,"errorMessage":"choosing base strategy","messagePattern":"choosing base strategy","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"src/directories.rs","lineNumber":38,"sourceCode":"fn state_dir(basedirs: &impl BaseStrategy) -> Option<PathBuf> {\n    xdg_dir(\"XDG_STATE_HOME\").or_else(|| basedirs.state_dir())\n}\n\nfn cache_dir(basedirs: &impl BaseStrategy) -> PathBuf {\n    xdg_dir(\"XDG_CACHE_HOME\").unwrap_or_else(|| basedirs.cache_dir())\n}\n\n/// A helper struct for directories in fnm that uses XDG Base Directory Specification\n/// if applicable for the platform.\n#[derive(Debug, Clone)]\npub struct Directories(\n    #[cfg(windows)] etcetera::base_strategy::Windows,\n    #[cfg(not(windows))] etcetera::base_strategy::Xdg,\n);\n\nimpl Default for Directories {\n    fn default() -> Self {\n        Self(etcetera::choose_base_strategy().expect(\"choosing base strategy\"))\n    }\n}\n\nimpl Directories {\n    pub fn strategy(&self) -> &impl BaseStrategy {\n        &self.0\n    }\n\n    pub fn default_base_dir(&self) -> PathBuf {\n        let strategy = self.strategy();\n        let modern = strategy.data_dir().join(\"fnm\");\n        if modern.exists() {\n            return modern;\n        }\n\n        let legacy = strategy.home_dir().join(\".fnm\");\n        if legacy.exists() {\n            return legacy;","sourceCodeStart":20,"sourceCodeEnd":56,"githubUrl":"https://github.com/Schniz/fnm/blob/86adc9676ceb2a509b21e75e74048b93c89f097d/src/directories.rs#L20-L56","documentation":"`Directories::default()` builds fnm's whole directory layout by calling `etcetera::choose_base_strategy()`, which must resolve the user's home directory (HOME on Unix, USERPROFILE on Windows). If that resolution fails, the Result is unwrapped with `.expect(\"choosing base strategy\")`, so every fnm command panics immediately at startup before any argument handling.","triggerScenarios":"Running any fnm command (`fnm list`, `fnm env`, ...) in a process where HOME/USERPROFILE is unset and the OS-level lookup also fails — `env -i fnm list`, a systemd unit/cron entry without `Environment=HOME=...`, a service account whose passwd entry has no usable home, or a minimal container.","commonSituations":"Docker images run with a scrubbed env; cron jobs and systemd timers (which start with a sparse environment); `sudo` into service users with env_reset; hardened CI runners that strip HOME; chroot/build sandboxes.","solutions":["Export a home explicitly: `export HOME=/root` (or for the service user) before invoking fnm.","For systemd units add `Environment=HOME=/var/lib/myuser`; for cron add `HOME=/root` at the top of the crontab.","In Dockerfiles set `ENV HOME=/root` (or create the user with a home via `useradd -m`).","If patching fnm: replace the expect with graceful error propagation or fall back to the current directory/stdout-only mode."],"exampleFix":"# before\nenv -i /usr/local/bin/fnm list   # panic: choosing base strategy\n\n# after\nenv -i HOME=/tmp/fnm-home /usr/local/bin/fnm list   # works (creates dirs under HOME)","handlingStrategy":"validation","validationCode":"fn home_env_present() -> bool {\n    let key = if cfg!(windows) { \"USERPROFILE\" } else { \"HOME\" };\n    std::env::var_os(key).is_some()\n}\n// run before any fnm invocation; otherwise export HOME=/tmp/fnm-home","typeGuard":null,"tryCatchPattern":"std::panic::catch_unwind(|| run_fnm(args))\n    .unwrap_or_else(|_| {\n        eprintln!(\"fnm crashed at startup: HOME/USERPROFILE is probably unset\");\n        std::process::exit(78); // EX_CONFIG\n    });","preventionTips":["Always set HOME (Unix) / USERPROFILE (Windows) in cron, systemd units, and containers.","In Dockerfiles, prefer `USER` with a created home (`useradd -m`).","Smoke-test `fnm --version` in the target environment during provisioning."],"tags":["rust","home-directory","environment-variable","startup","panic","etcetera"],"backgroundTag":"home-directory-not-found","analyzedSha":"86adc9676ceb2a509b21e75e74048b93c89f097d","analyzedAt":"2026-08-16T21:43:05.725Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}