{"record":{"id":"97a67c29232dd1b7","repo":"astrid-runtime/astrid","slug":"gateway-revocation-migration-receipt-raced-retry","errorCode":null,"errorMessage":"gateway revocation migration receipt raced; retry startup","messagePattern":"gateway revocation migration receipt raced; retry startup","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/astrid-gateway/src/revocations.rs","lineNumber":424,"sourceCode":"        schema: 1,\n        digest,\n        principal_count: entries.len(),\n    };\n    let encoded = serde_json::to_vec(&receipt).context(\"encode revocation migration receipt\")?;\n    let existing = store\n        .get(REVOCATION_NAMESPACE, MIGRATION_RECEIPT_KEY)\n        .await\n        .map_err(|error| anyhow::anyhow!(\"read revocation migration receipt: {error}\"))?;\n    if let Some(existing) = existing {\n        if existing != encoded {\n            anyhow::bail!(\"gateway revocation migration receipt conflicts\");\n        }\n    } else if !store\n        .compare_and_swap(REVOCATION_NAMESPACE, MIGRATION_RECEIPT_KEY, None, encoded)\n        .await\n        .map_err(|error| anyhow::anyhow!(\"write revocation migration receipt: {error}\"))?\n    {\n        anyhow::bail!(\"gateway revocation migration receipt raced; retry startup\");\n    }\n    let _ = load_from_store(store).await?;\n    match std::fs::remove_file(&path) {\n        Ok(()) => {},\n        Err(error) if error.kind() == std::io::ErrorKind::NotFound => {},\n        Err(error) => return Err(anyhow::anyhow!(\"retire {}: {error}\", path.display())),\n    }\n    Ok(true)\n}\n\n/// Spawn the audit-event watcher. Subscribes to the kernel's audit\n/// topic and updates the revocation map whenever a successful\n/// `AgentDelete` admin op lands. Detached: terminates when the bus\n/// is dropped (i.e. daemon shutdown), so no explicit join is needed.\n///\n/// `bus` is the kernel's shared event bus; `revoked_at` is the same\n/// `Arc<RwLock<…>>` held by [`crate::state::GatewayState`] so writes\n/// here become visible to every in-flight verify call.","sourceCodeStart":406,"sourceCodeEnd":442,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/revocations.rs#L406-L442","documentation":"Race guard in migrate_legacy_file: the compare_and_swap that installs the revocation migration receipt failed because another writer inserted a different receipt concurrently. The digest-bearing receipt encodes the migrated entry set; a mismatch means two different migration attempts raced, the on-disk state is ambiguous, and startup must be retried rather than proceeding on possibly-divergent revocation data.","triggerScenarios":"Thrown at crates/astrid-gateway/src/revocations.rs:424 when the library encounters an invalid state.","commonSituations":"See trigger scenarios.","solutions":["Retry startup — the winner's receipt should now be readable and migration complete","Verify the receipt and hydrated epochs are present before continuing","Ensure only one instance performs startup migration, or rely on the CAS fence"],"exampleFix":null,"handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":null,"preventionTips":[],"tags":[],"backgroundTag":null,"analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-17T15:17:12.973Z"}