astrid-runtime/astrid · error
exhausted unique names for quarantined leftover capsule…
Error message
exhausted unique names for quarantined leftover capsule authority receipts
What it means
unique_quarantine_path generates collision-free names for quarantined leftover receipts (encoding the original name and appending a numeric suffix). If it exhausts all candidate names without finding a free path, it bails with this error, refusing to overwrite an existing quarantined file.
Solutions
- Clean out stale entries in the migrations quarantine directory (review each file first — they are receipts), then re-run
- Manually rename existing quarantined files to shorter names to free candidate names
- If the original file name is extremely long, rename the leftover receipt to a short name before re-running the operation
- Do not delete or modify quarantined receipts that look unfamiliar; archive the whole quarantine dir and start fresh
Example fix
// before ~/.astrid/migrations/quarantine/ filled with receipt.json, receipt.json.1, ... receipt.json.N error: exhausted unique names for quarantined leftover capsule authority receipts // after $ mkdir ~/astrid-quarantine-archive $ mv ~/.astrid/migrations/quarantine/* ~/astrid-quarantine-archive/ $ astrid migrate # quarantine slot freed -> succeeds
Defensive patterns
Strategy: validation
Validate before calling
let qdir = home.migrations_dir().join("quarantine");
let collisions = std::fs::read_dir(&qdir)?.filter_map(Result::ok)
.filter(|e| e.file_name().to_string_lossy().starts_with("receipt"))
.count();
if collisions > 1000 { anyhow::bail!("quarantine dir saturated; clean it first"); } Try / catch
match quarantine_legacy_authority_receipt(&home, &path) {
Err(e) if e.to_string().contains("exhausted unique names") => {
// archive existing quarantine contents, then retry
},
other => other?,
} Prevention
- Archive and clear the quarantine directory after reviewing its contents following repeated migrations
- Keep original receipt file names short to avoid truncation-driven collisions
- Do not leave thousands of stale quarantined receipts in place
When it happens
Trigger: Calling quarantine_legacy_authority_receipt → unique_quarantine_path when the quarantine directory already contains files occupying every generated candidate name for a given receipt name (leftover.rs:288) — typically after many repeated quarantines of identically named receipts, or name-length truncation causing collisions.
Common situations: Repeated migration attempts quarantining the same-named receipt hundreds of times; a very long original file name forcing heavy truncation in encoded_file_name so all suffix variants collide; corrupted quarantine dir full of stale entries.
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
- quarantine to
- AlreadyExists
- AlreadyExists
- Astrid durable media is redirected or not a regular file
- Astrid home without a layout sentinel is redirected or not…
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/f26710a37d988877.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-capsule-install/src/authority/leftover.rs:288
fn unique_quarantine_path(root: &Path, file_name: &std::ffi::OsStr) -> anyhow::Result<PathBuf> {
let encoded = encoded_file_name(file_name);
for index in 0_u32..1024 {
let candidate = if index == 0 {
root.join(&encoded)
} else {
root.join(format!("{encoded}-{index}"))
};
match fs::symlink_metadata(&candidate) {
Err(error) if error.kind() == std::io::ErrorKind::NotFound => return Ok(candidate),
Ok(_) => {},
Err(error) => {
return Err(error).with_context(|| {
format!("inspect quarantine candidate {}", candidate.display())
});
},
}
}
bail!("exhausted unique names for quarantined leftover capsule authority receipts")
}
fn encoded_file_name(name: &std::ffi::OsStr) -> String {
const MAX_SAFE_NAME: usize = 80;
match name.to_str() {
Some(text)
if !text.is_empty()
&& text.len() <= MAX_SAFE_NAME
&& text != "."
&& text != ".."
&& text
.chars()
.all(|ch| ch.is_ascii_alphanumeric() || matches!(ch, '-' | '_' | '.')) =>
{
text.to_owned()
},
_ => format!("invalid-{}", blake3::hash(&os_str_bytes(name)).to_hex()),
}View on GitHub (pinned to affd8760f4)