astrid-runtime/astrid · error
gateway revocation storage is unavailable while a legacy…
Error message
gateway revocation storage is unavailable while a legacy revocation file exists
What it means
Fail-closed startup guard in hydrate_revocations: revocation storage (control KV) is unavailable while a legacy JSON revocation file still exists, so revocations could be silently lost after restart; startup aborts before exposing an unfenced listener.
Solutions
- Restore connectivity to the control KV store, then restart
- Complete the legacy-file migration so the KV is authoritative and the file can be retired
- If running standalone tests without KV, remove the legacy file first — no-KV mode is intentionally non-durable
Defensive patterns
Strategy: fallback
When it happens
Trigger: Thrown at crates/astrid-gateway/src/state.rs:385 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/4c4b52f59736b2e9.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-gateway/src/state.rs:385
}))
}
/// Hydrate principal and device revocation epochs from the authoritative
/// kernel control KV before the HTTP listener is exposed. A legacy JSON
/// index is imported exactly once when present and then retired after KV
/// receipt/read-back verification. Any list/get error or malformed value
/// aborts startup before an unfenced listener can be exposed. Standalone
/// route tests without a KV remain usable only when no legacy file exists;
/// that no-KV mode is intentionally non-durable across restart.
///
/// # Panics
///
/// Panics if either in-memory revocation lock is poisoned, indicating an
/// earlier panic while mutating gateway security state.
pub async fn hydrate_revocations(&self) -> anyhow::Result<()> {
let Some(store) = self.storage_kv.as_deref() else {
if crate::revocations::legacy_file_exists()? {
anyhow::bail!(
"gateway revocation storage is unavailable while a legacy revocation file exists"
);
}
return Ok(());
};
let _ = crate::revocations::migrate_legacy_file(store).await?;
let (principals, devices) = crate::revocations::load_from_store(store).await?;
*self
.revoked_at
.write()
.expect("revocation map poisoned during startup hydration") = principals;
*self
.revoked_key_ids
.write()
.expect("device revocation map poisoned during startup hydration") = devices;
Ok(())
}
View on GitHub (pinned to affd8760f4)