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
- Inspect the wrapped message ({e}) for the storage-level cause (permissions, missing file, corruption).
- Verify the audit log directory/files are readable by the gateway process and rotation policy isn't removing the active file.
- Repair or restore the audit log file; restart the gateway to reopen handles if needed.
- 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
- Ensure the audit directory and active file stay readable by the gateway uid.
- Coordinate rotation policy with open handles (rotate-then-reopen, not delete-in-place).
- Alert on disk/permission anomalies on the audit volume.
- Retry transient I/O errors with backoff before surfacing a 500.
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
- read durable capsule package '{id}': {error}
- unexpected {kind:?} in a canonical File owning closure
- equal chunk identities named different bytes in delta eviden
- encoded evidence delta did not reconstruct its target
- durable capsule {id} disappeared after publish
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/134ceb5b85d54705.
Report an issue: GitHub.