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

audit read failed: {e}

Error message

audit read failed: {e}

What it means

The audit handler wraps any failure from audit_log.get_session_entries() into an Internal (500) error carrying the underlying message ("audit read failed: {e}"). It means the audit storage backend failed while reading all entries for the session — an I/O or storage-layer error, not a client error.

Source

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

        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
    // disk is bounded by the operator's rotation policy; for a
    // routine workload (thousands of entries per day, not millions)
    // this is fine. Tracked as a perf follow-up if/when it bites.
    let all = audit_log
        .get_session_entries(&session_id)
        .await
        .map_err(|e| GatewayError::Internal(anyhow::anyhow!("audit read failed: {e}")))?;

    // Newest first. The storage backend already returns entries in
    // insertion order (oldest first), so a plain `reverse()` is both
    // cheaper (O(N) vs O(N log N) sort, zero allocation) AND
    // preserves true insertion order for entries that share a
    // second-granular timestamp — a stable sort with `Reverse` would
    // keep equal-ts entries oldest-first inside the slice, which
    // breaks the cursor's expectation that "page 1" lists every
    // entry strictly newer than page 2.
    let mut entries: Vec<AuditEntry> = all;
    entries.reverse();

    // Resolve an explicit filter only under post-read authority. Pagination
    // applies the current policy to each record, so narrowing takes effect at
    // the next record boundary.
    let firehose_at_query_boundary = super::events::caller_holds(
        &capability_probe,
        &caller_principal,

View on GitHub (pinned to affd8760f4)

Solutions

  1. Inspect the wrapped message ({e}) for the storage-level cause (permissions, missing file, corruption).
  2. Verify the audit log directory/files are readable by the gateway process and rotation policy isn't removing the active file.
  3. Repair or restore the audit log file; restart the gateway to reopen handles if needed.
  4. Retry the request if the failure was transient (busy disk, temporary I/O error).

Example fix

// before: reading a rotated-away file
$ ls /var/log/astrid/audit  # current session file missing
// after: keep active handle stable during rotation
let audit = RotatingAuditLog::open(&config.audit_path).await?; // reopen + retry on rotation
Defensive patterns

Strategy: retry

Validate before calling

// precheck: confirm audit file readable before querying
fs.accessSync(auditPath, fs.constants.R_OK);

Try / catch

match audit_log.get_session_entries(&session_id).await {
    Ok(entries) => entries,
    Err(e) => { warn!("audit read failed: {e}; retrying"); backoff_retry(|| audit_log.get_session_entries(&session_id)).await? }
}

Prevention

When it happens

Trigger: GET /api/audit when the underlying audit log storage (e.g. a rotated file on disk) fails to read the session's entries — read error, corrupt file, backend error.

Common situations: Audit file rotated/deleted out from under the running gateway; disk or permission problems on the audit directory; corrupted audit log file; storage backend returning an unexpected error mid-request.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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