astrid-runtime/astrid · error · GatewayError::Internal

gateway is not wired to a live event bus; admin operations…

Error message

gateway is not wired to a live event bus; admin operations unavailable

What it means

An admin bus operation was requested but the GatewayState was constructed without an event bus. BusAdminClient needs a live bus connection to reach the daemon; without it, no admin operation can proceed, so admin_client() returns GatewayError::Internal. In production the daemon always wires the bus, so this normally indicates a standalone/test or misconfigured gateway.

Solutions

  1. Wire the event bus into GatewayState at construction time (use the production constructor/daemon wiring path)
  2. Check gateway startup config so the event bus is initialized before routes are served
  3. Guard admin routes to return 503/404 when running in standalone mode instead of exercising the bus
  4. In tests, build the state with a live (in-memory) bus rather than the tests-only constructor

Example fix

// before
let state = GatewayState::new_standalone(config); // no event_bus
// after
let state = GatewayState::new(config, event_bus, session_id)?; // wired
Defensive patterns

Strategy: validation

Validate before calling

fn admin_ready(state: &GatewayState) -> bool {
    state.event_bus.is_some()
}

Type guard

fn wired_bus(state: &GatewayState) -> Option<Arc<EventBus>> {
    state.event_bus.clone()
}

Try / catch

match state.admin_client(caller) {
    Ok(client) => use_client(client).await,
    Err(GatewayError::Internal(e)) if e.to_string().contains("not wired to a live event bus") => {
        Err(StatusCode::SERVICE_UNAVAILABLE)
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling admin_client() (directly or via admin_client_for / assert_live_audit_revocation) on a GatewayState built by the tests-only constructor or a bootstrap path that leaves the event_bus field None.

Common situations: Running the gateway in standalone/dev mode without bus wiring; a configuration path that skips event-bus initialization; unit tests exercising admin endpoints against an unwired state.

Related errors


AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09). Data as JSON: /api/errors/1278b727c4d824da. Report an issue: GitHub.

Appendix: source

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

        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(|| {
            crate::error::GatewayError::Internal(anyhow::anyhow!(
                "gateway is not wired to a live event bus; admin operations unavailable"
            ))
        })?;
        let session_id = self.session_id.as_ref().ok_or_else(|| {
            crate::error::GatewayError::Internal(anyhow::anyhow!(
                "gateway is not wired to a live kernel session; admin operations unavailable"
            ))
        })?;
        Ok(crate::bus_admin::BusAdminClient::new(
            bus,
            caller,
            session_id.0,
        ))
    }

    /// Build a bus-direct admin client for an authenticated caller, carrying
    /// the caller's device scope through to the kernel cap-gate.
    ///

View on GitHub (pinned to affd8760f4)