{"record":{"id":"3ee0891ac4ec6b42","repo":"sinelaw/fresh","slug":"could-not-determine-cache-directory","errorCode":null,"errorMessage":"Could not determine cache directory","messagePattern":"Could not determine cache directory","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/fresh-editor/src/services/plugins/embedded.rs","lineNumber":68,"sourceCode":"/// Get the cache directory for extracted plugins\nfn get_cache_dir() -> Option<PathBuf> {\n    dirs::cache_dir().map(|p| p.join(\"fresh\").join(\"embedded-plugins\"))\n}\n\n/// Extract embedded plugins to the cache directory.\n///\n/// Concurrency contract: this function is called via a process-local\n/// `OnceLock`, but multiple test processes (e.g. cargo-nextest) may\n/// each call it concurrently against the same on-disk directory. We\n/// publish atomically: extract into a sibling `.pending.<pid>.<nanos>`\n/// directory, write a `.extracted` marker, then `rename` into place.\n/// `rename` over an existing non-empty directory fails on POSIX, so\n/// only one publisher wins; losers fall back to the winner's\n/// directory. Readers gate on the marker file so they never observe a\n/// half-extracted tree.\nfn extract_plugins() -> Result<PathBuf, std::io::Error> {\n    let cache_base = get_cache_dir().ok_or_else(|| {\n        std::io::Error::new(\n            std::io::ErrorKind::NotFound,\n            \"Could not determine cache directory\",\n        )\n    })?;\n\n    let content_hash = PLUGINS_CONTENT_HASH.trim();\n    let cache_dir = cache_base.join(content_hash);\n    let marker = cache_dir.join(\".extracted\");\n\n    if marker.exists() {\n        tracing::info!(\"Using cached embedded plugins from: {:?}\", cache_dir);\n        return Ok(cache_dir);\n    }\n\n    tracing::info!(\"Extracting embedded plugins to: {:?}\", cache_dir);\n    std::fs::create_dir_all(&cache_base)?;\n\n    let pid = std::process::id();","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/sinelaw/fresh/blob/67894ca5463dbd7a89bb31add4627c27d6b79d83/crates/fresh-editor/src/services/plugins/embedded.rs#L50-L86","documentation":"extract_plugins needs a base cache directory to atomically stage extracted embedded plugins (via rename). If get_cache_dir() returns None (no XDG_CACHE_HOME, no HOME, and no fallback the helper recognizes), it returns io::ErrorKind::NotFound 'Could not determine cache directory'.","triggerScenarios":"Calling get_embedded_plugins_dir()/extract_plugins() in an environment where the cache directory cannot be resolved: service accounts without HOME, sanitized environment (systemd, cron, containers), or XDG_CACHE_HOME set to an empty/invalid value.","commonSituations":"Running the editor under systemd or a container runtime that strips HOME/XDG vars; CI jobs with minimal env; launching via a wrapper script that clears the environment.","solutions":["Set XDG_CACHE_HOME (or HOME) to a writable directory before launching","Launch the editor with a proper user environment (avoid env -i / sanitized service units without Environment=HOME=...)","Make the caller handle the None case with an explicit fallback (e.g. std::env::temp_dir())","Pre-extract plugins so the runtime extraction path is not needed"],"exampleFix":"// before\nstd::process::Command::new(\"fresh-editor\").env_clear().spawn()?;\n// after\nstd::process::Command::new(\"fresh-editor\")\n    .env(\"XDG_CACHE_HOME\", \"/home/dev/.cache\")\n    .spawn()?;","handlingStrategy":"fallback","validationCode":"fn cache_dir_resolvable() -> bool {\n    std::env::var_os(\"XDG_CACHE_HOME\").filter(|v| !v.is_empty()).is_some()\n        || std::env::var_os(\"HOME\").filter(|v| !v.is_empty()).is_some()\n}","typeGuard":null,"tryCatchPattern":"match get_embedded_plugins_dir() {\n    Ok(dir) => dir,\n    Err(e) if e.kind() == io::ErrorKind::NotFound => {\n        std::env::set_var(\"XDG_CACHE_HOME\", \"/tmp/fresh-cache\");\n        get_embedded_plugins_dir()?\n    }\n    Err(e) => return Err(e.into()),\n}","preventionTips":["Always set HOME/XDG_CACHE_HOME in systemd units, cron jobs, and containers","Avoid launching with a fully sanitized environment (env -i)","Provide an explicit CLI/env override for the cache directory"],"tags":["environment","cache","xdg","plugins"],"backgroundTag":"missing-env-var","analyzedSha":"67894ca5463dbd7a89bb31add4627c27d6b79d83","analyzedAt":"2026-09-13T15:04:03.701Z","contentChangedAt":"2026-09-13T15:04:03.701Z","schemaVersion":2},"datasetVersion":"2026-09-20T23:17:15.980Z"}