{"record":{"id":"f8f0db85eb2705f0","repo":"libnyanpasu/clash-nyanpasu","slug":"failed-to-promote-runtime-config-error","errorCode":null,"errorMessage":"failed to promote runtime config: {error}","messagePattern":"failed to promote runtime config: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/client/runtime.rs","lineNumber":118,"sourceCode":"}\n\n#[derive(Debug, Clone, Default)]\npub struct RuntimeLifecycleState {\n    pub promoted: Option<Arc<RuntimeSnapshot>>,\n}\n\npub(crate) async fn write_product(product: &Path, bytes: &[u8]) -> anyhow::Result<()> {\n    if let Some(parent) = product.parent() {\n        tokio::fs::create_dir_all(parent).await?;\n    }\n    let product = product.to_path_buf();\n    let bytes = bytes.to_vec();\n    tokio::task::spawn_blocking(move || {\n        atomicwrites::AtomicFile::new(&product, atomicwrites::OverwriteBehavior::AllowOverwrite)\n            .write(|file| std::io::Write::write_all(file, &bytes))\n    })\n    .await?\n    .map_err(|error| anyhow::anyhow!(\"failed to promote runtime config: {error}\"))?;\n    Ok(())\n}\n\n#[derive(Debug, Clone)]\npub struct RuntimePaths {\n    product: Utf8PathBuf,\n    candidate_dir: Utf8PathBuf,\n}\n\nimpl RuntimePaths {\n    pub fn from_resolver(paths: &PathResolver) -> anyhow::Result<Self> {\n        let runtime_dir = utf8_path(paths.app_config_dir().join(RUNTIME_CONFIG_DIR))?;\n        Ok(Self {\n            product: runtime_dir.join(RUNTIME_CONFIG),\n            candidate_dir: runtime_dir.join(\".candidates\"),\n        })\n    }\n","sourceCodeStart":100,"sourceCodeEnd":136,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/client/runtime.rs#L100-L136","documentation":"`write_product` promotes the rendered runtime config to its final path using the `atomicwrites` crate inside `spawn_blocking`; if the atomic write fails (I/O error from create/rename/fsync), the error is wrapped with this message. The promotion step is the last, all-or-nothing step of publishing a runtime config.","triggerScenarios":"Calling the runtime config build/commit path that reaches `write_product` (backend/tauri/src/client/runtime.rs:118) when the target directory does not exist or is not writable, the disk is full, an antivirus/handle holds the file open, or a permission error occurs on rename.","commonSituations":"Runtime config directory deleted or locked by another process (editor, sync client like OneDrive/Dropbox, another app instance); read-only filesystem; disk quota exceeded; Windows file locking by the mihomo core process reading the previous config.","solutions":["Check the wrapped `{error}` message and verify the product directory exists and is writable (create it: the tests `write_product_creates_the_runtime_directory` show it is expected to be created)","Close processes locking the target file (antivirus, file-sync, the running core) and retry","Free disk space / fix quota if the error indicates ENOSPC","Run the app with sufficient permissions for the config directory, or move the config dir out of a protected location"],"exampleFix":"// before\nstd::fs::remove_dir_all(runtime_dir).ok(); // deletes dir while writer expects it or its parent\nwrite_product(...).await?;\n// after\nstd::fs::create_dir_all(runtime_dir)?; // ensure promotion target parent exists\nwrite_product(...).await?;","handlingStrategy":"try-catch","validationCode":"// Pre-check promotion target writability\nlet dir = product_path.parent().ok_or(\"no parent dir\")?;\nstd::fs::create_dir_all(dir)?;\nlet probe = dir.join(\".write-probe\");\nstd::fs::write(&probe, b\"\")?;\nstd::fs::remove_file(&probe)?;","typeGuard":null,"tryCatchPattern":"match write_product(&product, bytes).await {\n    Err(e) if e.to_string().contains(\"failed to promote runtime config\") => {\n        // inspect source error: check disk space, file locks (antivirus/sync/core process), permissions\n        eprintln!(\"runtime config promotion failed: {e}\");\n    }\n    Err(e) => return Err(e),\n    Ok(()) => (),\n}","preventionTips":["Ensure the runtime config directory exists before committing","Exclude the config directory from antivirus real-time locking and file-sync tools","Avoid deleting or moving the runtime directory while the app is running","Keep adequate free disk space; handle ENOSPC explicitly"],"tags":["io","file-write","atomic","config"],"backgroundTag":"file-write-failed","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}