astrid-runtime/astrid · critical

device revocation map poisoned during startup hydration

Error message

device revocation map poisoned during startup hydration

What it means

Same poisoning scenario as the `revoked_at` map, but for the `revoked_key_ids` map (per-device key revocations). `hydrate_revocations` panics if that `RwLock` is poisoned when installing the device revocation list loaded from the store. The device map must be populated before the gateway serves requests, so failure is fatal.

Solutions

  1. Fix the upstream panic in code that holds the `revoked_key_ids` lock.
  2. Make lock-protected mutation paths infallible or return errors instead of panicking under the lock.
  3. Use `unwrap_or_else(PoisonError::into_inner)` in hydration if overwriting with fresh store data is verified safe.
  4. Sequence hydration before request-handling threads start so nothing else can poison the lock first.

Example fix

// before
*self.revoked_key_ids.write().expect("device revocation map poisoned during startup hydration") = devices;
// after
let mut guard = self
    .revoked_key_ids
    .write()
    .unwrap_or_else(std::sync::PoisonError::into_inner);
*guard = devices;
Defensive patterns

Strategy: try-catch

Validate before calling

// Rust: check poison before installing device revocations
let guard = self.revoked_key_ids.write();
if guard.is_err() { log::error!("revoked_key_ids poisoned; fixing root cause required"); }

Try / catch

// Rust: recover-by-overwrite when hydration data is authoritative
*self.revoked_key_ids.write()
    .unwrap_or_else(std::sync::PoisonError::into_inner) = devices;

Prevention

When it happens

Trigger: Calling `hydrate_revocations` when the `revoked_key_ids` `RwLock` returns `Err(PoisonError)` because a prior thread panicked while holding it.

Common situations: A panic in code that iterates device key revocations (e.g. while verifying a request key id) leaves the lock poisoned; a later startup hydration or reload then fails with this message.

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/6100f5956851c428. Report an issue: GitHub.

Appendix: source

Thrown at crates/astrid-gateway/src/state.rs:400

    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(())
    }

    /// Build a bus-direct admin client bound to `caller`. Routes
    /// hosted in this same process talk to the kernel over the
    /// shared event bus rather than the Unix socket — bypasses the
    /// `astrid-capsule-cli` proxy entirely and removes the 19 RPS
    /// admin-throughput ceiling the socket path imposes.
    ///
    /// # Errors
    /// Returns an internal error if the state was built without a
    /// live event bus (the standalone tests-only constructor). In
    /// production the daemon always wires it up.
    pub fn admin_client(
        &self,
        caller: astrid_core::PrincipalId,
    ) -> Result<crate::bus_admin::BusAdminClient, crate::error::GatewayError> {
        let bus = self.event_bus.clone().ok_or_else(|| {

View on GitHub (pinned to affd8760f4)