Skip to content

Emit llm_call_failed audit event on LLM error path — failed calls are invisible to audit #361

Description

@initializ-mk

Problem — a failed LLM call leaves no audit trace

Field report 2026-07-22: an agent whose tool schema the provider rejected failed every task with a gateway 400 — but the audit stream showed nothing. EmitLLMCall is wired only to the AfterLLMCall success hook (forge-cli/runtime/runner.go); the error path just logs "agent loop error" to the pod (runner.go:2461). Result: the Security Events screen showed healthy llm_call rows from before the break and then silence — the one signal that explains "agent up, all tasks failing" lived only in kubectl logs.

Change

Emit an llm_call_failed audit event from the LLM error path (naming per the <entity>_<verb> convention, joining llm_call / llm_call_cancelled):

  • model, provider, duration_ms as on llm_call
  • fields.error: bounded (e.g. first 512 bytes) of the provider/gateway error — enough to carry "Property keys should match pattern …" into the audit stream
  • fields.status: upstream HTTP status when known
  • outcome: error

Cross-repo registration (contract)

  • security-next: add llm_call_failed to models/audit_event_model.go::AuditEventTypes
  • console-next: typed label + row summary (render the bounded error like other failure rows)

Paired PRs per the workspace contract rule.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions