astrid-runtime/astrid · error

durable capsule {id} disappeared after publish

Error message

durable capsule {id} disappeared after publish

What it means

Thrown by migrate_native_capsules_with_report after a durable capsule is installed into the registry: the immediate get_snapshot readback returned None, meaning the capsule that was just published cannot be found in the registry. This is an internal invariant check — installation reported success but persistence did not survive, so the migration aborts rather than leaving a half-migrated capsule.

Source

Thrown at crates/astrid-capsule-install/src/storage/migration.rs:183

            .with_context(|| format!("verify canonical legacy capsule archive {id}"))?;
        let mut durable_authority = authority;
        verification
            .content_digest()
            .clone_into(&mut durable_authority.content_digest);
        let durable_authority_bytes = serde_json::to_vec_pretty(&durable_authority)
            .with_context(|| format!("serialize durable legacy capsule authority {id}"))?;
        let package = CapsulePackage::new(archive, meta_bytes, durable_authority_bytes);
        let expectation = match registry.get_snapshot(&owner, id)? {
            None => CapsuleInstallExpectation::Absent,
            Some(snapshot) if snapshot.package() == &package => {
                CapsuleInstallExpectation::Generation(snapshot.generation())
            },
            Some(_) => bail!("durable capsule {id} conflicts with legacy native content"),
        };
        registry.install(&owner, id, &package, expectation)?;
        let readback = registry
            .get_snapshot(&owner, id)?
            .ok_or_else(|| anyhow::anyhow!("durable capsule {id} disappeared after publish"))?;
        if readback.package() != &package {
            bail!("durable capsule {id} failed byte-for-byte readback");
        }
        read_verified_durable_package_for_owner(store, &owner, id)?.ok_or_else(|| {
            anyhow::anyhow!("durable capsule {id} failed authoritative verification")
        })?;
        astrid_core::platform_fs::verify_no_redirects(&target)
            .with_context(|| format!("verify legacy capsule {id} before retirement"))?;
        let final_archive = canonical_legacy_archive(home, &target, &meta, &manifest)?;
        if final_archive != package.archive {
            bail!("legacy capsule {id} changed before retirement");
        }
        if fs::read(target.join("meta.json"))? != package.metadata {
            bail!("legacy capsule {id} metadata changed before retirement");
        }
        if read_installed_authority_bytes(home, &target)?.as_deref()
            != Some(source_authority_bytes.as_slice())
        {

View on GitHub (pinned to affd8760f4)

Solutions

  1. Re-run the migration after checking the storage backend for corruption or incomplete writes; the install itself succeeded so state may just need re-publishing
  2. Verify no concurrent process (GC, compaction, another migration) is running against the same capsule home directory
  3. Test the storage backend: call install() then get_snapshot() manually and confirm the snapshot survives
  4. If using a custom RuntimePrincipalStore/registry implementation, fix it so install() is durably persisted before returning
Defensive patterns

Strategy: try-catch

Validate before calling

// before migrating
let pre = registry.get_snapshot(&owner, &id)?;
if pre.is_none() { /* legacy capsule exists but nothing durable yet — proceed */ }
// verify storage writable
std::fs::metadata(store.root())?;

Try / catch

match migrate_native_capsules(&home) {
    Err(e) if e.to_string().contains("disappeared after publish") => {
        // storage dropped the install: check backend health, then retry once
        retry_migration_with_fresh_store()?;
    }
    Err(e) => return Err(e),
    Ok(report) => apply(report),
}

Prevention

When it happens

Trigger: Calling migrate_native_capsules / migrate_all_native_capsules_with_report (directly or via the migration tests) when registry.install() succeeds but registry.get_snapshot(&owner, id) returns None for the same owner/id — i.e. the durable store dropped or failed to durably record the entry between write and read.

Common situations: Corrupt or partially-flushed storage backend during migration; concurrent process deleting/compacting the registry while migration runs; a bug in a custom storage implementation that fails to persist install() state; mismatched StateOwner scoping so the snapshot lookup hits a different namespace.

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/4467d4153e1651c4. Report an issue: GitHub.