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

gateway is not wired to a live audit log; historical-query u

Error message

gateway is not wired to a live audit log; historical-query unavailable

What it means

The historical audit-query endpoint returns this Internal (500) error when either state.audit_log or state.session_id is None. Both must be present because the handler reads entries for the caller's session from the audit log; a gateway started without an audit log (or without a session id) cannot serve historical queries.

Source

Thrown at crates/astrid-gateway/src/routes/audit.rs:215

    )
)]
pub async fn get_audit(
    State(state): State<Arc<GatewayState>>,
    Query(query): Query<AuditQuery>,
    req: Request<axum::body::Body>,
) -> GatewayResult<Json<AuditQueryResponse>> {
    let caller = caller_from(&req)?;
    let capability_probe = req
        .extensions()
        .get::<super::events::CapabilityProbe>()
        .cloned()
        .unwrap_or_else(super::events::CapabilityProbe::deny_all);
    let caller_principal = caller.principal.clone();

    let (audit_log, session_id) = match (state.audit_log.as_ref(), state.session_id.as_ref()) {
        (Some(log), Some(sid)) => (log.clone(), sid.clone()),
        _ => {
            return Err(GatewayError::Internal(anyhow::anyhow!(
                "gateway is not wired to a live audit log; historical-query unavailable"
            )));
        },
    };

    let limit = match query.limit {
        Some(l) if l > MAX_LIMIT => {
            return Err(GatewayError::BadRequest(format!(
                "limit {l} exceeds the cap of {MAX_LIMIT}"
            )));
        },
        Some(0) | None => DEFAULT_LIMIT,
        Some(l) => l,
    };

    // Pull the full session slice from the audit log. The audit log
    // doesn't expose an "after cursor" query primitive today, so we
    // fetch + filter + paginate in-process. The persistent log on

View on GitHub (pinned to affd8760f4)

Solutions

  1. Start the gateway with a live audit log wired into GatewayState (audit_log: Some(...)).
  2. Ensure session_id is set on GatewayState before serving traffic.
  3. Fix any audit-backend initialization failure that left audit_log as None, then restart.
  4. Don't expose the /api/audit route on deployments that intentionally run without an audit log.

Example fix

// before
let state = GatewayState { audit_log: None, session_id: None, .. };
// after
let audit = RotatingAuditLog::open(&config.audit_path).await?;
let state = GatewayState { audit_log: Some(Arc::new(audit)), session_id: Some(session_id), .. };
Defensive patterns

Strategy: fallback

Validate before calling

// check audit capability before querying
const cap = await fetch('/api/audit/_capability');
if (!cap.ok) return []; // historical query unsupported on this node

Type guard

fn audit_available(state: &GatewayState) -> bool {
    state.audit_log.is_some() && state.session_id.is_some()
}

Try / catch

try {
  const entries = await fetch('/api/audit?limit=100');
} catch (e) {
  if (/historical-query unavailable/.test(e.message)) return []; // no audit backend on this node
  throw e;
}

Prevention

When it happens

Trigger: GET /api/audit (get_audit) on a gateway where audit_log or session_id in GatewayState is None — i.e. the server was built without a live audit log or session identity.

Common situations: Gateway launched in a mode that disables audit logging; audit backend failed to open at startup so audit_log stayed None; querying audit history on a freshly restarted node whose session_id hasn't been established.

Related errors


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