{"record":{"id":"b8b73f310bfb6721","repo":"astrid-runtime/astrid","slug":"cannot-retire-leftover-capsule-authority-while-pre","errorCode":null,"errorMessage":"cannot retire leftover capsule authority while previous transaction artifact exists at {}","messagePattern":"cannot retire leftover capsule authority while previous transaction artifact exists at (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/authority/leftover.rs","lineNumber":56,"sourceCode":"    };\n    AuthorityReceiptTransaction::stage(home, target_dir, &receipt)?.commit()?;\n    Ok(())\n}\n\n/// Active leftover receipts that are not workspace-portal targets.\npub(crate) fn unmatched_active_receipts(\n    home: &AstridHome,\n    workspace_targets: &[PathBuf],\n) -> anyhow::Result<Vec<PathBuf>> {\n    let status = super::legacy_authority_receipt_status(home, workspace_targets)?;\n    if !status.pending.is_empty() {\n        bail!(\n            \"cannot retire leftover capsule authority while pending transaction artifact exists at {}\",\n            status.pending[0].display()\n        );\n    }\n    if !status.previous.is_empty() {\n        bail!(\n            \"cannot retire leftover capsule authority while previous transaction artifact exists at {}\",\n            status.previous[0].display()\n        );\n    }\n    Ok(status.unknown_active)\n}\n\n/// Parse a regular-file leftover receipt. Invalid JSON returns `Ok(None)`.\npub(crate) fn parse_legacy_authority_receipt(\n    path: &Path,\n) -> anyhow::Result<Option<(InstalledAuthority, Vec<u8>)>> {\n    let metadata = fs::symlink_metadata(path)\n        .with_context(|| format!(\"inspect leftover capsule authority {}\", path.display()))?;\n    if metadata.file_type().is_symlink() || !metadata.is_file() {\n        bail!(\n            \"leftover capsule authority receipt is not a regular file: {}\",\n            path.display()\n        );","sourceCodeStart":38,"sourceCodeEnd":74,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/authority/leftover.rs#L38-L74","documentation":"Like the pending check, `unmatched_active_receipts` refuses to sweep when a *previous* receipt-transaction artifact exists. The previous-generation file is the backup of the receipt that a transaction intended to replace; its presence means a transaction has not fully completed (or crashed between commit and cleanup), and retiring leftover receipts now could destroy data needed for recovery.","triggerScenarios":"Calling `unmatched_active_receipts` (via `retire_unmatched_legacy_authority_receipts`) while `legacy_authority_receipt_status(...).previous` is non-empty — a `.previous` transaction artifact remains at the reported path, typically from an interrupted or crashed receipt commit.","commonSituations":"Crash or SIGKILL during the two-phase receipt transaction leaving the previous-generation file uncleaned; disk-full during commit cleanup; concurrent install/migration racing the sweep.","solutions":["Complete transaction recovery: restore/commit or safely discard the previous-generation artifact, then rerun the sweep","If no transaction is active and the artifact is stale, remove it per the documented recovery procedure and retry","Serialize the leftover sweep with installs/migrations so transactions can never be mid-flight"],"exampleFix":"// before\nbail!(\"cannot retire leftover capsule authority while previous transaction artifact exists at {}\", status.previous[0].display());\n// after: finish the interrupted transaction first\n// AuthorityReceiptTransaction::recover(home)?;\n// let leftover = unmatched_active_receipts(home, &workspace_targets)?;","handlingStrategy":"try-catch","validationCode":"// Check for previous-generation transaction artifacts before sweeping\nlet status = legacy_authority_receipt_status(&home, &workspace_targets)?;\nif !status.previous.is_empty() {\n    // finish or discard the interrupted transaction before sweeping\n}","typeGuard":null,"tryCatchPattern":"match unmatched_active_receipts(&home, &targets) {\n    Err(e) if e.to_string().contains(\"previous transaction artifact\") => {\n        AuthorityReceiptTransaction::recover(&home)?;\n        unmatched_active_receipts(&home, &targets)\n    },\n    other => other,\n}","preventionTips":["Run transaction recovery after any crash or SIGKILL of an install","Ensure adequate disk space so commit cleanup can't fail midway","Avoid running migration/sweep concurrently with installs","Alert on .previous artifacts persisting in the authority directory"],"tags":["transaction","authority-receipt","migration","state-conflict"],"backgroundTag":"invalid-state-transition","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}