{"record":{"id":"ec1adeb1659bb0fa","repo":"libnyanpasu/clash-nyanpasu","slug":"failed-to-allocate-a-unique-runtime-candidate-afte","errorCode":null,"errorMessage":"failed to allocate a unique runtime candidate after 16 attempts","messagePattern":"failed to allocate a unique runtime candidate after 16 attempts","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"backend/tauri/src/client/runtime.rs","lineNumber":194,"sourceCode":"                    use std::os::unix::fs::OpenOptionsExt;\n                    options.mode(0o600);\n                }\n                match options.open(&path) {\n                    Ok(mut file) => {\n                        file.write_all(&bytes)?;\n                        file.sync_all()?;\n                        let bytes_sha256 = Sha256::digest(&bytes).into();\n                        return Ok(CandidateFile {\n                            path,\n                            bytes_sha256,\n                            cleaned: false,\n                        });\n                    }\n                    Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => continue,\n                    Err(error) => return Err(error.into()),\n                }\n            }\n            anyhow::bail!(\"failed to allocate a unique runtime candidate after 16 attempts\")\n        })\n        .await?\n    }\n\n    pub async fn cleanup_stale_candidates(&self, max_age: Duration) -> anyhow::Result<usize> {\n        prepare_private_dir(&self.candidate_dir).await?;\n        let now = SystemTime::now();\n        let mut removed = 0;\n        let mut entries = tokio::fs::read_dir(&self.candidate_dir).await?;\n        while let Some(entry) = entries.next_entry().await? {\n            let name = entry.file_name();\n            if !name.to_string_lossy().starts_with(\"candidate-\") {\n                continue;\n            }\n            let metadata = tokio::fs::symlink_metadata(entry.path()).await?;\n            if is_symlink_or_reparse(&metadata) || !metadata.is_file() {\n                continue;\n            }","sourceCodeStart":176,"sourceCodeEnd":212,"githubUrl":"https://github.com/libnyanpasu/clash-nyanpasu/blob/f7dbce2997c633e484f54788035e770b3ee99773/backend/tauri/src/client/runtime.rs#L176-L212","documentation":"Raised in RuntimeStore::create_candidate_with_names (backend/tauri/src/client/runtime.rs:194) when all 16 pre-generated unique random candidate filenames hit AlreadyExists on exclusive create (create_new(true)). Each attempt uses a fresh nanoid name, so exhausting all 16 implies something is deeply wrong with the candidate directory or the name space is pathologically saturated.","triggerScenarios":"create_candidate() loops over 16 nanoid-based names; every OpenOptions::create_new(true) open returns ErrorKind::AlreadyExists and the loop falls through to the bail.","commonSituations":"An attacker or misbehaving process pre-filling the candidate dir with predictable candidate-*.yaml files; a broken/aliased directory view (e.g. the dir is actually a file or a special filesystem that reports AlreadyExists); astronomically unlikely true nanoid collisions after many stale candidates.","solutions":["Inspect the candidate directory and remove stale candidate-*.yaml files (or call cleanup_stale_candidates).","Verify the candidate directory is a real private directory (not symlinked, not a special FS).","Re-run the operation; a single occurrence is almost certainly not a real collision storm.","If it recurs, check for another process racing to create the same files and coordinate ownership of the directory."],"exampleFix":"// before\nstore.create_candidate(&bytes).await?;\n// after\nstore.cleanup_stale_candidates(Duration::from_secs(3600)).await?;\nstore.create_candidate(&bytes).await?;","handlingStrategy":"retry","validationCode":"// Before creating candidates, prune stale ones\nlet removed = store.cleanup_stale_candidates(Duration::from_secs(3600)).await?;\nlog::debug!(\"pruned {removed} stale runtime candidates\");","typeGuard":null,"tryCatchPattern":"match store.create_candidate(&bytes).await {\n    Ok(c) => c,\n    Err(e) if e.to_string().contains(\"after 16 attempts\") => {\n        store.cleanup_stale_candidates(Duration::from_secs(3600)).await?;\n        store.create_candidate(&bytes).await?\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Schedule periodic cleanup_stale_candidates with a sane max_age","Ensure only one process owns the candidate directory","Verify the candidate dir is a real private directory, not a symlink or special FS","Treat repeated exhaustion as an integrity alarm, not a collision coincidence"],"tags":["filesystem","collision","runtime","rust"],"backgroundTag":"file-already-exists","analyzedSha":"f7dbce2997c633e484f54788035e770b3ee99773","analyzedAt":"2026-09-08T01:24:59.197Z","contentChangedAt":"2026-09-08T01:24:59.197Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}