astrid-runtime/astrid · error

capsule {} disappeared during durable contracts scan

Error message

capsule {} disappeared during durable contracts scan

What it means

durable_contracts_pin iterates the authoritative capsule registry for an owner and reads each capsule's verified durable package. If registry.list returns a summary whose package cannot be read back (read_verified_durable_package_for_owner returns None), the registry is inconsistent — a listed capsule vanished mid-scan — so the library aborts with this error instead of computing a skew verdict from incomplete data.

Source

Thrown at crates/astrid-capsule-install/src/contracts.rs:91

                == Some(CONTRACTS_WIT_BASENAME)
        })
        .min_by(|a, b| a.0.cmp(b.0))
        .map(|(_, hash)| hash)
}

/// Return the plurality of the daemon fleet's contracts pins from its authoritative owner-root registry.
/// The package metadata and archive are read from one durable snapshot per
/// capsule; no native `.local/capsules` directory or cache is consulted.
pub fn durable_contracts_pin(
    store: &RuntimePrincipalStore,
    owner: &StateOwner,
) -> anyhow::Result<Option<String>> {
    let registry = store.capsules();
    let mut counts = std::collections::BTreeMap::<String, usize>::new();
    for summary in registry.list(owner)? {
        let Some(package) = read_verified_durable_package_for_owner(store, owner, summary.id())?
        else {
            bail!(
                "capsule {} disappeared during durable contracts scan",
                summary.id()
            );
        };
        let Some(pin) = contracts_pin(&package.metadata().wit_files) else {
            continue;
        };
        if !is_blake3_pin(pin) {
            bail!(
                "durable capsule {} has malformed contracts pin",
                summary.id()
            );
        }
        let Some(relative) = package
            .metadata()
            .wit_files
            .keys()
            .filter(|relative| {

View on GitHub (pinned to affd8760f4)

Solutions

  1. Re-run the contracts refresh once no other astrid operation is mutating the store — transient races resolve on retry
  2. Run the store's repair/prune command (or reinstall the missing capsule) to reconcile the registry with the durable packages
  3. Check disk space and store-directory permissions; an interrupted write can leave a summary without a package
  4. Back up and rebuild the principal store if the inconsistency persists across runs

Example fix

// before
$ astrid contracts refresh   # while another terminal runs: astrid uninstall my-capsule
error: capsule my-capsule disappeared during durable contracts scan
// after
$ astrid uninstall my-capsule   # finish first
$ astrid contracts refresh      # single writer -> succeeds
Defensive patterns

Strategy: retry

Validate before calling

// ensure no concurrent store writers before scanning
let lock = acquire_store_lock(owner)?; // advisory exclusive lock across astrid processes
let ids: Vec<_> = store.capsules().list(owner)?.iter().map(|s| s.id().to_string()).collect();
for id in &ids {
    if read_verified_durable_package_for_owner(store, owner, id)?.is_none() {
        anyhow::bail!("package missing for {} before scan", id);
    }
}

Try / catch

loop {
    match durable_contracts_pin(store, owner) {
        Ok(pin) => break Ok(pin),
        Err(e) if e.to_string().contains("disappeared during durable contracts scan") && retries < 3 => {
            retries += 1;
            std::thread::sleep(Duration::from_millis(200 * retries));
        },
        Err(e) => break Err(e),
    }
}

Prevention

When it happens

Trigger: Calling refresh_canonical_contracts_from_registry → durable_contracts_pin when a capsule summary exists in the registry but its durable package is unreadable/unverifiable at contracts.rs:89-95 — e.g. the store was mutated concurrently, the package blob was deleted, or verification failed and returned None.

Common situations: A concurrent capsule uninstall/prune racing the contracts refresh; corrupted or manually pruned capsule store directories; disk-full/interrupted write leaving a registry entry without its package; differing store versions across replicas.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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