astrid-runtime/astrid · error

cannot retire leftover capsule authority while pending trans

Error message

cannot retire leftover capsule authority while pending transaction artifact exists at {}

What it means

`unmatched_active_receipts` (in the legacy/relocated-home leftover sweep) enumerates capsule-authority receipts that no longer correspond to any workspace target so they can be retired or quarantined. This error is thrown when the sweep finds a pending receipt-transaction artifact (an in-progress transaction file), which means a prior receipt write may be incomplete; retiring leftovers before that transaction resolves could corrupt authority state.

Source

Thrown at crates/astrid-capsule-install/src/authority/leftover.rs:50

    if read_installed_authority(home, target_dir)?.is_some() {
        return Ok(());
    }
    let Some(receipt) = unique_relocated_receipt(home, target_dir, manifest, workspace_targets)?
    else {
        return Ok(());
    };
    AuthorityReceiptTransaction::stage(home, target_dir, &receipt)?.commit()?;
    Ok(())
}

/// Active leftover receipts that are not workspace-portal targets.
pub(crate) fn unmatched_active_receipts(
    home: &AstridHome,
    workspace_targets: &[PathBuf],
) -> anyhow::Result<Vec<PathBuf>> {
    let status = super::legacy_authority_receipt_status(home, workspace_targets)?;
    if !status.pending.is_empty() {
        bail!(
            "cannot retire leftover capsule authority while pending transaction artifact exists at {}",
            status.pending[0].display()
        );
    }
    if !status.previous.is_empty() {
        bail!(
            "cannot retire leftover capsule authority while previous transaction artifact exists at {}",
            status.previous[0].display()
        );
    }
    Ok(status.unknown_active)
}

/// Parse a regular-file leftover receipt. Invalid JSON returns `Ok(None)`.
pub(crate) fn parse_legacy_authority_receipt(
    path: &Path,
) -> anyhow::Result<Option<(InstalledAuthority, Vec<u8>)>> {
    let metadata = fs::symlink_metadata(path)

View on GitHub (pinned to affd8760f4)

Solutions

  1. Resolve the pending transaction first: let the interrupted install finish or commit/roll back the staged `AuthorityReceiptTransaction`, then rerun the sweep
  2. If the pending artifact is definitively stale (no install running), remove or finalize the pending file per the transaction-recovery procedure, then retry
  3. Ensure the sweep is not run concurrently with an in-flight install holding a receipt transaction

Example fix

// before
bail!("cannot retire leftover capsule authority while pending transaction artifact exists at {}", status.pending[0].display());
// after: recover the transaction before sweeping
// AuthorityReceiptTransaction::recover(home)?;  // commit or roll back pending artifact
// let leftover = unmatched_active_receipts(home, &workspace_targets)?;
Defensive patterns

Strategy: try-catch

Validate before calling

// Check for pending transaction artifacts before sweeping leftovers
let status = legacy_authority_receipt_status(&home, &workspace_targets)?;
if !status.pending.is_empty() {
    // resolve the pending transaction (recover/commit) before sweeping
}

Try / catch

match unmatched_active_receipts(&home, &targets) {
    Err(e) if e.to_string().contains("pending transaction artifact") => {
        AuthorityReceiptTransaction::recover(&home)?;
        unmatched_active_receipts(&home, &targets)
    },
    other => other,
}

Prevention

When it happens

Trigger: Calling `unmatched_active_receipts` (via `retire_unmatched_legacy_authority_receipts` or the leftover-sweep tests) while `legacy_authority_receipt_status(...).pending` is non-empty — i.e. a staged but uncommitted `AuthorityReceiptTransaction` artifact exists in the authority directory (e.g. after a crash mid-transaction).

Common situations: A previous install/rebind crashed or was killed while committing a receipt transaction; a stale `.pending` file left behind by an interrupted process; running the migration/sweep concurrently with an active install.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/09d1db5f2a743d89. Report an issue: GitHub.