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

gateway is not wired to a live event bus; kernel requests…

Error message

gateway is not wired to a live event bus; kernel requests unavailable

What it means

A kernel request (e.g. capsule metadata lookup) was attempted but the GatewayState has no event bus wired. kernel_client_for() needs the bus to reach the daemon kernel and returns GatewayError::Internal when the bus is absent.

Solutions

  1. Construct GatewayState with a live event bus via the production wiring path
  2. Verify the gateway bootstrap actually initializes the bus before accepting requests
  3. Return a clear 503 from kernel-backed routes when running standalone
  4. In tests, supply an in-memory bus to the state constructor

Example fix

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

Strategy: validation

Validate before calling

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

Type guard

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

Try / catch

match state.kernel_client_for(&caller) {
    Ok(client) => capsule_metadata_for(client).await,
    Err(GatewayError::Internal(e)) if e.to_string().contains("kernel requests unavailable") => {
        Err(StatusCode::SERVICE_UNAVAILABLE)
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Calling kernel_client_for() (used by capsule_metadata_for) on a state built without an event bus — the standalone/tests-only constructor or a config path that skips bus initialization.

Common situations: Dev/standalone gateway mode without a daemon; misconfigured bootstrap that never creates the bus; tests hitting kernel-backed routes on an unwired state.

Related errors


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

Appendix: source

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

            .with_device_key_id(caller.device_key_id.clone()))
    }

    /// Build a bus-direct kernel client bound to an authenticated caller.
    ///
    /// HTTP routes have already verified the bearer token and resolved the
    /// caller/device scope. They should not re-enter the external socket
    /// handshake, because the gateway intentionally does not possess arbitrary
    /// agent private keys and would be downgraded to `anonymous`.
    ///
    /// # Errors
    /// Returns an internal error if the state was built without a live event
    /// bus (the standalone tests-only constructor).
    pub fn kernel_client_for(
        &self,
        caller: &crate::auth::CallerContext,
    ) -> Result<crate::bus_kernel::BusKernelClient, 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; kernel requests 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; kernel requests unavailable"
            ))
        })?;
        Ok(
            crate::bus_kernel::BusKernelClient::new(bus, caller.principal.clone(), session_id.0)
                .with_device_key_id(caller.device_key_id.clone()),
        )
    }
}

#[cfg(test)]
mod tests {
    use super::*;

View on GitHub (pinned to affd8760f4)