{"record":{"id":"ad17e841a9aa36a9","repo":"astrid-runtime/astrid","slug":"legacy-capsule-id-authority-receipt-disappeared","errorCode":null,"errorMessage":"legacy capsule {id} authority receipt disappeared","messagePattern":"legacy capsule (.+?) authority receipt disappeared","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-capsule-install/src/storage/migration.rs","lineNumber":162,"sourceCode":"            bail!(\"legacy capsule metadata version differs for {id}\");\n        }\n        // Released pre-authority installs are admitted only through the\n        // existing one-time verifier. It pins their exact manifest,\n        // capabilities, and executable before any durable publication.\n        // Relocated homes keep receipts hashed from a previous absolute\n        // path; rebind a unique leftover onto this target first.\n        rebind_relocated_legacy_authority_receipt(home, &target, &manifest, workspace_targets)\n            .with_context(|| format!(\"rebind relocated leftover authority for {id}\"))?;\n        verify_installed_authority(home, &target, &manifest)\n            .with_context(|| format!(\"verify legacy capsule authority {id}\"))?;\n        let authority = read_installed_authority(home, &target)?.ok_or_else(|| {\n            anyhow::anyhow!(\"legacy capsule {id} authority verification produced no receipt\")\n        })?;\n        if authority.capsule_id != id || authority.version != manifest.package.version {\n            bail!(\"legacy capsule authority identity differs for {id}\");\n        }\n        let source_authority_bytes = read_installed_authority_bytes(home, &target)?\n            .ok_or_else(|| anyhow::anyhow!(\"legacy capsule {id} authority receipt disappeared\"))?;\n        let archive = canonical_legacy_archive(home, &target, &meta, &manifest)?;\n        let verification = artifact::verify_archive_bytes(&archive)\n            .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)?;","sourceCodeStart":144,"sourceCodeEnd":180,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-capsule-install/src/storage/migration.rs#L144-L180","documentation":"Immediately after reading and validating the authority receipt, migration reads the raw receipt bytes with `read_installed_authority_bytes` to embed into the durable record. If that second read returns `None`, the receipt that existed moments earlier has disappeared — either deleted concurrently or due to a state mismatch between the two read functions. The error protects against building a durable authority from mismatched or vanishing data.","triggerScenarios":"Calling `migrate_native_capsules_with_report` when a concurrent process deletes or rewrites the authority receipt between `read_installed_authority` and `read_installed_authority_bytes`; or when the two functions disagree on the receipt's location/format so the byte read finds nothing where the structured read succeeded.","commonSituations":"Two capsule migrations or an install running in parallel on the same home directory; an antivirus/cleanup tool quarantining files mid-migration; a customized receipt path layout confusing one of the read helpers after a version change.","solutions":["Ensure only one migration/install process runs against a given capsule home at a time (file lock or single-flight wrapper)","Re-run the migration once the concurrent writer has finished; the transient race resolves on retry","Check that `read_installed_authority` and `read_installed_authority_bytes` resolve the same receipt path for your install layout (especially after path-hashed receipt relocation changes) and align them"],"exampleFix":"// before\nlet (a, b) = tokio::join!(migrate_native_capsules(home), reinstall_capsule(home)); // concurrent delete/recreate\n\n// after\nlet lock = home.lock(\"migration\")?;\nlet report = migrate_native_capsules(home)?;\ndrop(lock);","handlingStrategy":"try-catch","validationCode":"fn assert_no_concurrent_writers(home: &Path) -> anyhow::Result<()> {\n    let lock_path = home.join(\".migration.lock\");\n    anyhow::ensure!(!lock_path.exists(),\n        \"another migration/install appears to be running (lock present)\");\n    std::fs::write(&lock_path, std::process::id().to_string())?;\n    Ok(())\n}","typeGuard":"fn receipt_readable_twice(home: &Path, target: &Path) -> bool {\n    matches!(read_installed_authority(home, target), Ok(Some(_)))\n        && matches!(read_installed_authority_bytes(home, target), Ok(Some(_)))\n}","tryCatchPattern":"match migrate_native_capsules(home) {\n    Err(e) if e.to_string().contains(\"receipt disappeared\") => {\n        // transient race or path-layout mismatch: serialize access, then retry once\n        let _lock = acquire_home_lock(home)?;\n        migrate_native_capsules(home)?\n    }\n    Err(e) => return Err(e),\n    Ok(r) => r,\n}","preventionTips":["Hold an exclusive lock on the capsule home for the entire migration/install","Never run a capsule install concurrently with a legacy migration on the same home","Exclude capsule directories from antivirus/cleanup tools that quarantine files mid-read","After receipt-path layout changes, confirm read_installed_authority and read_installed_authority_bytes resolve identical paths"],"tags":["migration","authority","receipt","race-condition","rust"],"backgroundTag":"file-not-found","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"}