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

daemon kernel-request: {e}

Error message

daemon kernel-request: {e}

What it means

`daemon_kernel_error` is the shared mapper from `KernelClientError` to `GatewayError` used by gateway route handlers. If the kernel client error is a timeout it becomes `GatewayError::Timeout`, otherwise the whole error is wrapped as `GatewayError::Internal` with `daemon kernel-request: {e}`. It indicates the request to the daemon/kernel failed outside of normal application-level error responses.

Source

Thrown at crates/astrid-gateway/src/routes/mod.rs:62

/// Map a bus-direct / socket kernel-request failure ([`KernelClientError`]) to a
/// [`GatewayError`]. Single-sourced so every `kernel_client_for(...).request()`
/// call site maps consistently.
///
/// A [`Timeout`](KernelClientError::Timeout) — the daemon was slow / wedged, not
/// a transport fault — maps to **504** so callers can distinguish "still
/// processing, retry" (e.g. a heavy `InstallCapsule` under load) from a genuine
/// 500. Connection loss, bus shutdown, build, and decode failures all map to
/// **500**. A kernel-side rejection is a `KernelResponse::Error` handled at the
/// call site (→ 403), never reaching this path.
#[allow(
    clippy::needless_pass_by_value,
    reason = "consumed by Display formatting"
)]
pub(crate) fn daemon_kernel_error(e: KernelClientError) -> GatewayError {
    if e.is_timeout() {
        return GatewayError::Timeout(format!("daemon kernel-request: {e}"));
    }
    GatewayError::Internal(anyhow::anyhow!("daemon kernel-request: {e}"))
}

pub mod agent;
pub mod audit;
pub mod auth;
pub mod caps;
#[cfg(test)]
mod capsule_sources;
pub mod capsules;
pub mod distribution;
pub mod env;
pub mod events;
pub mod groups;
pub mod invites;
pub mod models;
pub mod observability;
pub mod principals;
pub mod quotas;

View on GitHub (pinned to affd8760f4)

Solutions

  1. Confirm the daemon/kernel process is up and the configured endpoint matches (socket path/URL).
  2. Read the inner `{e}` in logs to classify: connection vs channel-closed vs serialization.
  3. Align gateway and daemon versions; redeploy together after protocol changes.
  4. For timeouts specifically, tune the kernel request timeout budget and check daemon load.

Example fix

// before
GatewayError::Internal(anyhow::anyhow!("daemon kernel-request: {e}"))
// after
if e.is_connect() {
    return Err(GatewayError::ServiceUnavailable(format!("daemon unreachable: {e}")));
}
GatewayError::Internal(anyhow::anyhow!("daemon kernel-request: {e}"))
Defensive patterns

Strategy: retry

Validate before calling

// before issuing the request
if !daemon_health_probe().await {
    return Err(ServiceUnavailable("kernel daemon not ready"));
}

Type guard

fn is_kernel_transport_failure(e: &KernelClientError) -> bool {
    !e.is_timeout() && (e.is_connect() || e.is_channel_closed())
}

Try / catch

match route_call(state, req).await {
    Err(e) if is_kernel_transport_failure(&e) => {
        // one bounded retry after backoff
        tokio::time::sleep(RETRY_DELAY).await;
        route_call(state, req).await
    }
    r => r,
}

Prevention

When it happens

Trigger: Any gateway route using this mapper when the kernel client call fails with a non-timeout `KernelClientError`: connection refused, channel closed, serialization failure, or daemon-side transport fault.

Common situations: Daemon not started before the gateway; kernel socket path misconfigured in env/config; daemon crashed mid-request; version mismatch of the kernel IPC protocol after upgrading one side only.

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/73708c02c3efbe96. Report an issue: GitHub.