{"record":{"id":"2759be8e1d5648e9","repo":"Spotifyd/spotifyd","slug":"failed-to-determine-cache-directory-please-specif","errorCode":null,"errorMessage":"Failed to determine cache directory, please specify one manually","messagePattern":"Failed to determine cache directory, please specify one manually","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/config.rs","lineNumber":519,"sourceCode":"            }\n        })?;\n\n        // The call to get_merged_sections consumes the FileConfig!\n        if let Some(merged_sections) = config_content.get_merged_sections() {\n            self.shared_config.merge_with(merged_sections);\n        }\n\n        Ok(())\n    }\n}\n\nimpl SharedConfigValues {\n    pub fn get_cache(&self, for_oauth: bool) -> color_eyre::Result<Cache> {\n        let Some(cache_path) = self.cache_path.as_deref().map(Cow::Borrowed).or_else(|| {\n            ProjectDirs::from(\"\", \"\", \"spotifyd\")\n                .map(|dirs| Cow::Owned(dirs.cache_dir().to_path_buf()))\n        }) else {\n            bail!(\"Failed to determine cache directory, please specify one manually\");\n        };\n\n        if for_oauth {\n            let mut creds_path = cache_path.into_owned();\n            creds_path.push(\"oauth\");\n            Cache::new(Some(creds_path), None, None, None)\n        } else {\n            let audio_cache = !self.no_audio_cache.unwrap_or(false);\n\n            let mut creds_path = cache_path.to_path_buf();\n            creds_path.push(\"zeroconf\");\n            Cache::new(\n                Some(creds_path.as_path()),\n                Some(cache_path.as_ref()),\n                audio_cache.then_some(cache_path.as_ref()),\n                self.max_cache_size,\n            )\n        }","sourceCodeStart":501,"sourceCodeEnd":537,"githubUrl":"https://github.com/Spotifyd/spotifyd/blob/6af1e48d9fe481b8a72296fdd22916e47d72c746/src/config.rs#L501-L537","documentation":"Spotifyd's SharedConfigValues::get_cache needs a directory to store the librespot Cache (credentials, audio, data). It first uses the explicitly configured `cache_path`, otherwise it asks the `dirs` crate's ProjectDirs for the platform cache dir (e.g. XDG_CACHE_HOME on Linux). If `cache_path` is unset AND ProjectDirs::from returns None (no HOME/XDG environment available), it bails with this error.","triggerScenarios":"Calling get_cache (directly or via spotifyd startup) when: (1) `cache_path` is not set in the spotifyd config file, and (2) ProjectDirs::from(\"\", \"\", \"spotifyd\") returns None because $HOME (and/or $XDG_CACHE_HOME) is unset or empty — e.g. running as a systemd service without Environment/HOME, in a container, via cron, or under an environment stripped of vars.","commonSituations":"Running spotifyd under systemd/launchd/Docker where HOME is not set; starting spotifyd from a non-login shell or supervisord with a sanitized environment; users who never set `cache_path` in spotifyd.conf assuming a default location exists; running as a system user with no home directory.","solutions":["Set `cache_path = \"/path/to/cache\"` in the spotifyd config file (e.g. ~/.config/spotifyd/spotifyd.conf)","Ensure the $HOME environment variable is set when launching spotifyd (e.g. add Environment=HOME=%h to the systemd unit or export HOME before starting)","Set XDG_CACHE_HOME to a writable directory if HOME cannot be fixed","Run spotifyd as a user that has a valid home directory rather than a system/no-login user"],"exampleFix":"// before (systemd unit, HOME stripped)\n[Service]\nExecStart = /usr/bin/spotifyd --no-daemon\n\n// after\n[Service]\nEnvironment = HOME=%h\nExecStart = /usr/bin/spotifyd --no-daemon\n\n// or in spotifyd.conf\n// before\nglobal = { }\n// after\nglobal = { cache_path = \"/home/user/.cache/spotifyd\" }","handlingStrategy":"validation","validationCode":"// before starting spotifyd / calling get_cache\nif std::env::var(\"HOME\").map_or(true, |h| h.is_empty()) {\n    eprintln!(\"HOME is unset: set cache_path in spotifyd.conf or fix the environment\");\n}\n// or check config:\nlet cache_path_set = config.cache_path.is_some();","typeGuard":"fn has_cache_dir(cfg: &SharedConfigValues) -> bool {\n    cfg.cache_path.is_some()\n        || ProjectDirs::from(\"\", \"\", \"spotifyd\")\n            .map(|d| d.cache_dir().exists())\n            .unwrap_or(false)\n}","tryCatchPattern":"match config.get_cache(false) {\n    Ok(cache) => { /* use cache */ }\n    Err(e) if e.to_string().contains(\"cache directory\") => {\n        eprintln!(\"Set `cache_path` in your spotifyd config: {e}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Always set an explicit cache_path in spotifyd.conf for headless deployments (systemd, Docker, cron)","Verify HOME/XDG_CACHE_HOME are present in service unit environments","Create the cache directory and grant write permission to the spotifyd user","Test spotifyd startup with `systemctl --user` or a stripped env to catch missing HOME early"],"tags":["config","cache","environment","rust","spotifyd"],"backgroundTag":"missing-config-value","analyzedSha":"6af1e48d9fe481b8a72296fdd22916e47d72c746","analyzedAt":"2026-09-09T12:26:40.469Z","contentChangedAt":"2026-09-09T12:26:40.469Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}