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

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

Error message

gateway is not wired to a live kernel session; kernel requests unavailable

What it means

A kernel request was attempted with an event bus present but no kernel session id. BusKernelClient requires the session id to route requests to the kernel session, so kernel_client_for() returns GatewayError::Internal when session_id is None.

Solutions

  1. Complete the kernel session handshake and store the session id before serving kernel-backed routes
  2. Investigate startup errors from session registration and fix the root cause
  3. Gate routes on readiness so requests wait until state.session_id is Some
  4. Correct the state construction to pass the established session id

Example fix

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

Strategy: validation

Validate before calling

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

Type guard

fn session_id(state: &GatewayState) -> Option<SessionId> {
    state.session_id.clone()
}

Try / catch

if state.session_id.is_none() {
    return Err(StatusCode::SERVICE_UNAVAILABLE);
}
let client = state.kernel_client_for(&caller)?;

Prevention

When it happens

Trigger: Calling kernel_client_for() when event_bus is Some but session_id is None — typically a request racing the session handshake or a construction site that never stored the session id.

Common situations: Requests served before kernel session registration completes; failed/skipped session handshake in custom gateway embeddings; test setups wiring a bus but no session.

Related errors


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

Appendix: source

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

    /// 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::*;

    #[test]
    fn rate_limiter_blocks_within_window() {
        let mut limiter = RedeemRateLimiter::default();
        let ip: IpAddr = "127.0.0.1".parse().unwrap();

View on GitHub (pinned to affd8760f4)