{"record":{"id":"134ceb5b85d54705","repo":"astrid-runtime/astrid","slug":"audit-read-failed-e","errorCode":null,"errorMessage":"audit read failed: {e}","messagePattern":"audit read failed: (.+?)","errorType":"http","errorClass":"GatewayError::Internal","httpStatus":500,"severity":"error","filePath":"crates/astrid-gateway/src/routes/audit.rs","lineNumber":240,"sourceCode":"        Some(l) if l > MAX_LIMIT => {\n            return Err(GatewayError::BadRequest(format!(\n                \"limit {l} exceeds the cap of {MAX_LIMIT}\"\n            )));\n        },\n        Some(0) | None => DEFAULT_LIMIT,\n        Some(l) => l,\n    };\n\n    // Pull the full session slice from the audit log. The audit log\n    // doesn't expose an \"after cursor\" query primitive today, so we\n    // fetch + filter + paginate in-process. The persistent log on\n    // disk is bounded by the operator's rotation policy; for a\n    // routine workload (thousands of entries per day, not millions)\n    // this is fine. Tracked as a perf follow-up if/when it bites.\n    let all = audit_log\n        .get_session_entries(&session_id)\n        .await\n        .map_err(|e| GatewayError::Internal(anyhow::anyhow!(\"audit read failed: {e}\")))?;\n\n    // Newest first. The storage backend already returns entries in\n    // insertion order (oldest first), so a plain `reverse()` is both\n    // cheaper (O(N) vs O(N log N) sort, zero allocation) AND\n    // preserves true insertion order for entries that share a\n    // second-granular timestamp — a stable sort with `Reverse` would\n    // keep equal-ts entries oldest-first inside the slice, which\n    // breaks the cursor's expectation that \"page 1\" lists every\n    // entry strictly newer than page 2.\n    let mut entries: Vec<AuditEntry> = all;\n    entries.reverse();\n\n    // Resolve an explicit filter only under post-read authority. Pagination\n    // applies the current policy to each record, so narrowing takes effect at\n    // the next record boundary.\n    let firehose_at_query_boundary = super::events::caller_holds(\n        &capability_probe,\n        &caller_principal,","sourceCodeStart":222,"sourceCodeEnd":258,"githubUrl":"https://github.com/astrid-runtime/astrid/blob/affd8760f44190dbdfbec23403f4c4b642c33112/crates/astrid-gateway/src/routes/audit.rs#L222-L258","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before: reading a rotated-away file\n$ ls /var/log/astrid/audit  # current session file missing\n// after: keep active handle stable during rotation\nlet audit = RotatingAuditLog::open(&config.audit_path).await?; // reopen + retry on rotation","handlingStrategy":"retry","validationCode":"// precheck: confirm audit file readable before querying\nfs.accessSync(auditPath, fs.constants.R_OK);","typeGuard":null,"tryCatchPattern":"match audit_log.get_session_entries(&session_id).await {\n    Ok(entries) => entries,\n    Err(e) => { warn!(\"audit read failed: {e}; retrying\"); backoff_retry(|| audit_log.get_session_entries(&session_id)).await? }\n}","preventionTips":["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."],"tags":["rust","audit","io","storage"],"backgroundTag":"file-read-failed","analyzedSha":"affd8760f44190dbdfbec23403f4c4b642c33112","analyzedAt":"2026-09-09T21:28:12.402Z","contentChangedAt":"2026-09-09T21:28:12.402Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}