{"record":{"id":"4467d4153e1651c4","repo":"astrid-runtime/astrid","slug":"durable-capsule-id-disappeared-after-publish-4467d4","errorCode":null,"errorMessage":"durable capsule {id} disappeared after publish","messagePattern":"durable capsule (.+?) disappeared after publish","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage/migration.rs","lineNumber":183,"sourceCode":"            .with_context(|| format!(\"verify canonical legacy capsule archive {id}\"))?;\n        let mut durable_authority = authority;\n        verification\n            .content_digest()\n            .clone_into(&mut durable_authority.content_digest);\n        let durable_authority_bytes = serde_json::to_vec_pretty(&durable_authority)\n            .with_context(|| format!(\"serialize durable legacy capsule authority {id}\"))?;\n        let package = CapsulePackage::new(archive, meta_bytes, durable_authority_bytes);\n        let expectation = match registry.get_snapshot(&owner, id)? {\n            None => CapsuleInstallExpectation::Absent,\n            Some(snapshot) if snapshot.package() == &package => {\n                CapsuleInstallExpectation::Generation(snapshot.generation())\n            },\n            Some(_) => bail!(\"durable capsule {id} conflicts with legacy native content\"),\n        };\n        registry.install(&owner, id, &package, expectation)?;\n        let readback = registry\n            .get_snapshot(&owner, id)?\n            .ok_or_else(|| anyhow::anyhow!(\"durable capsule {id} disappeared after publish\"))?;\n        if readback.package() != &package {\n            bail!(\"durable capsule {id} failed byte-for-byte readback\");\n        }\n        read_verified_durable_package_for_owner(store, &owner, id)?.ok_or_else(|| {\n            anyhow::anyhow!(\"durable capsule {id} failed authoritative verification\")\n        })?;\n        astrid_core::platform_fs::verify_no_redirects(&target)\n            .with_context(|| format!(\"verify legacy capsule {id} before retirement\"))?;\n        let final_archive = canonical_legacy_archive(home, &target, &meta, &manifest)?;\n        if final_archive != package.archive {\n            bail!(\"legacy capsule {id} changed before retirement\");\n        }\n        if fs::read(target.join(\"meta.json\"))? != package.metadata {\n            bail!(\"legacy capsule {id} metadata changed before retirement\");\n        }\n        if read_installed_authority_bytes(home, &target)?.as_deref()\n            != Some(source_authority_bytes.as_slice())\n        {","sourceCodeStart":165,"sourceCodeEnd":201,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage/migration.rs#L165-L201","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["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","Verify no concurrent process (GC, compaction, another migration) is running against the same capsule home directory","Test the storage backend: call install() then get_snapshot() manually and confirm the snapshot survives","If using a custom RuntimePrincipalStore/registry implementation, fix it so install() is durably persisted before returning"],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// before migrating\nlet pre = registry.get_snapshot(&owner, &id)?;\nif pre.is_none() { /* legacy capsule exists but nothing durable yet — proceed */ }\n// verify storage writable\nstd::fs::metadata(store.root())?;","typeGuard":null,"tryCatchPattern":"match migrate_native_capsules(&home) {\n    Err(e) if e.to_string().contains(\"disappeared after publish\") => {\n        // storage dropped the install: check backend health, then retry once\n        retry_migration_with_fresh_store()?;\n    }\n    Err(e) => return Err(e),\n    Ok(report) => apply(report),\n}","preventionTips":["Do not run GC/compaction or a second migration concurrently with the migration","Health-check the storage backend (write/read round-trip) before starting migration","Run migration as the same OS user that owns the capsule store"],"tags":["rust","storage","migration","persistence"],"backgroundTag":"internal-invariant-violation","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"}