libnyanpasu/clash-nyanpasu · error
failed to allocate a unique runtime candidate after 16 attem
Error message
failed to allocate a unique runtime candidate after 16 attempts
What it means
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.
Source
Thrown at backend/tauri/src/client/runtime.rs:194
use std::os::unix::fs::OpenOptionsExt;
options.mode(0o600);
}
match options.open(&path) {
Ok(mut file) => {
file.write_all(&bytes)?;
file.sync_all()?;
let bytes_sha256 = Sha256::digest(&bytes).into();
return Ok(CandidateFile {
path,
bytes_sha256,
cleaned: false,
});
}
Err(error) if error.kind() == std::io::ErrorKind::AlreadyExists => continue,
Err(error) => return Err(error.into()),
}
}
anyhow::bail!("failed to allocate a unique runtime candidate after 16 attempts")
})
.await?
}
pub async fn cleanup_stale_candidates(&self, max_age: Duration) -> anyhow::Result<usize> {
prepare_private_dir(&self.candidate_dir).await?;
let now = SystemTime::now();
let mut removed = 0;
let mut entries = tokio::fs::read_dir(&self.candidate_dir).await?;
while let Some(entry) = entries.next_entry().await? {
let name = entry.file_name();
if !name.to_string_lossy().starts_with("candidate-") {
continue;
}
let metadata = tokio::fs::symlink_metadata(entry.path()).await?;
if is_symlink_or_reparse(&metadata) || !metadata.is_file() {
continue;
}View on GitHub (pinned to f7dbce2997)
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.
Example fix
// before store.create_candidate(&bytes).await?; // after store.cleanup_stale_candidates(Duration::from_secs(3600)).await?; store.create_candidate(&bytes).await?;
Defensive patterns
Strategy: retry
Validate before calling
// Before creating candidates, prune stale ones
let removed = store.cleanup_stale_candidates(Duration::from_secs(3600)).await?;
log::debug!("pruned {removed} stale runtime candidates"); Try / catch
match store.create_candidate(&bytes).await {
Ok(c) => c,
Err(e) if e.to_string().contains("after 16 attempts") => {
store.cleanup_stale_candidates(Duration::from_secs(3600)).await?;
store.create_candidate(&bytes).await?
}
Err(e) => return Err(e),
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.
Related errors
- runtime candidate directory is a symlink or reparse point: {
- runtime candidate path is not a private directory: {path}
- materialization has multiple staged resources
- staged file is not a regular file
- staged symlink specification is not a regular file
AI-assisted analysis of libnyanpasu/clash-nyanpasu@f7dbce2997 (2026-09-08).
Data as JSON: /api/errors/ec1adeb1659bb0fa.
Report an issue: GitHub.