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

gateway is not wired to a live kernel session; admin…

Error message

gateway is not wired to a live kernel session; admin operations unavailable

What it means

An admin bus operation was requested while the gateway has an event bus but no kernel session id. BusAdminClient requires the session id to address the live kernel session, so admin_client() returns GatewayError::Internal when session_id is None.

Solutions

  1. Ensure the kernel session handshake completes and stores the session id before serving admin routes
  2. Check startup logs for session registration failures and fix the underlying connect error
  3. Delay/readiness-gate admin endpoints until state.session_id is Some
  4. Fix the construction site to pass the session id alongside the event bus

Example fix

// before
let state = GatewayState::new(config, event_bus, None)?;
// after
let session = connect_kernel_session(&event_bus).await?;
let state = GatewayState::new(config, event_bus, Some(session.id()))?;
Defensive patterns

Strategy: validation

Validate before calling

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

Type guard

fn live_session(state: &GatewayState) -> Option<SessionId> {
    state.session_id.as_ref().map(|s| s.0)
}

Try / catch

if state.session_id.is_none() {
    return Err(StatusCode::SERVICE_UNAVAILABLE); // session not established yet
}
let client = state.admin_client(caller)?;

Prevention

When it happens

Trigger: Calling admin_client() / admin_client_for() / assert_live_audit_revocation() on a state where event_bus is Some but session_id is None — e.g. the bus was wired before the gateway completed its kernel session handshake, or the handshake failed silently.

Common situations: Admin requests arriving during startup before session establishment; the kernel session registration step failed or was skipped in a custom embedding of the gateway; test setups that set a bus but never a session id.

Related errors


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

Appendix: source

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

    /// 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.
    ///
    /// Use this for every admin op behind the auth middleware: it stamps the
    /// caller's `device_key_id` (when the bearer was device-scoped) onto each
    /// outbound request so a paired device's scope is enforced kernel-side.
    /// The two unauthenticated redeem routes (which act as the bootstrap
    /// `default` principal) keep [`admin_client`](Self::admin_client) — their

View on GitHub (pinned to affd8760f4)