astrid-runtime/astrid · error
leftover capsule authority receipt changed before retirement
Error message
leftover capsule authority receipt changed before retirement: {} What it means
retire_unmatched_authority_receipt_file re-reads the leftover receipt immediately before fs::remove_file and compares it to the expected bytes captured earlier. If they differ, the file was mutated between ingestion/quarantine and retirement, so deleting it would destroy data the library cannot vouch for — hence the error.
Source
Thrown at crates/astrid-capsule-install/src/authority/leftover.rs:170
path.display()
)
})?;
if metadata.file_type().is_symlink() || !metadata.is_file() {
bail!(
"leftover capsule authority receipt is not a regular file: {}",
path.display()
);
}
astrid_core::platform_fs::verify_no_redirects(path).with_context(|| {
format!(
"verify leftover capsule authority receipt {}",
path.display()
)
})?;
let actual = fs::read(path)
.with_context(|| format!("read leftover capsule authority {}", path.display()))?;
if actual != expected_bytes {
bail!(
"leftover capsule authority receipt changed before retirement: {}",
path.display()
);
}
fs::remove_file(path).with_context(|| {
format!(
"retire leftover capsule authority receipt {}",
path.display()
)
})?;
if let Some(parent) = path.parent() {
sync_authority_directory(parent)?;
}
Ok(())
}
fn unique_relocated_receipt(
home: &AstridHome,View on GitHub (pinned to affd8760f4)
Solutions
- Close other processes and re-run the operation so the read-then-delete window is uncontended
- Re-run from scratch: the tool re-reads the (now stable) receipt and retires it against fresh expected bytes
- If the change was intentional, re-run ingestion so the new bytes become the expected bytes before retirement
- Check filesystem/journal health if corruption rather than concurrent writes is suspected
Example fix
// before $ astrid migrate & # plus a second instance editing the receipt error: leftover capsule authority receipt changed before retirement // after $ pkill -f astrid $ astrid migrate # single process, stable file -> succeeds
Defensive patterns
Strategy: validation
Validate before calling
let before = std::fs::read(path)?; // pass `before` as expected_bytes and ensure no other process writes between read and retire assert!(retire_unmatched_authority_receipt_file(path, &before).is_ok());
Type guard
fn receipt_unchanged(p: &Path, expected: &[u8]) -> bool {
std::fs::read(p).map(|a| a == expected).unwrap_or(false)
} Try / catch
if let Err(e) = retire_unmatched_authority_receipt_file(path, &expected) {
if e.to_string().contains("changed before retirement") {
// re-read fresh bytes and re-ingest before retiring
let fresh = std::fs::read(path)?;
// re-ingest(fresh) then retire against fresh
} else { return Err(e); }
} Prevention
- Serialize all astrid operations on a home directory (file lock or single process)
- Exclude the authority directory from editors/daemons that auto-save
- Verify receipt stability (two reads equal) before invoking retirement
When it happens
Trigger: Calling retire_one_leftover → retire_unmatched_authority_receipt_file when the file content at leftover.rs:167-174 changed after the expected bytes were captured: a concurrent writer (another tool instance, sync daemon, editor) modified the receipt during the ingest-then-retire window.
Common situations: Two astrid processes running concurrently on the same home; a text editor or sync tool auto-saving the receipt; antivirus quarantining/rewriting the file mid-operation.
Understand the failure class
Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.
Related errors
- quarantined capsule authority bytes changed: {}
- projected file changed while read: {}
- workspace capsule manifest changed while it was being read:
- capsule materialization target is redirected: {error}
- read capsule projection entry: {error}
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/58c6673763010760.
Report an issue: GitHub.