Skip to content

Preserve and display classified agent turn failures instead of collapsing all failures to transient "Turn error" #1659

Description

@cameronhotchkies

Summary

Agent turn failures are being collapsed to a generic, transient "Turn error" indicator in the UI that disappears before the user can act on it. The system already has enough information to distinguish failure classes, but that information is discarded at two seams:

  1. Harness boundaryemit_turn_error in crates/buzz-relay/src/lib.rs serializes turn outcomes to JSON but does not include an error_class field, so all failure types arrive at the UI as undifferentiated errors.
  2. UI storeactiveAgentTurnsStore.ts handles turn_error / agent_panic by immediately ending the turn with no persistent error state, so the failure badge vanishes before the user can read it.

Root Cause

Part 1 — Harness (crates/buzz-relay/src/lib.rs)

The emit_turn_error closure (~line 2785) captures result.outcome but emits only outcome: "error". The PromptOutcome variants already carry enough information to classify the failure:

Variant Suggested error_class
PromptOutcome::AgentExited "exited"
PromptOutcome::Timeout "timeout"
PromptOutcome::Error(AcpError::IdleTimeout) "idle_timeout"
PromptOutcome::Error(AcpError::HardTimeout) "hard_timeout"
PromptOutcome::Error(AcpError::AgentError) "agent_error" ← Codex auth-expiry lands here (crates/buzz-relay/src/acp.rs:866)
PromptOutcome::Error(AcpError::Json) "protocol"
transport errors (via existing is_transport_error check ~line 2867) "transport"

Fix: add a string error_class field to the existing serde_json::json! payload at ~lines 2791–2794. No new types needed.

Part 2 — UI Store (desktop/src/activeAgentTurnsStore.ts)

// current (~lines 342–352)
case "turn_error":
case "agent_panic":
  endTurn(...)  // deletes immediately, no error trace left

Fix: add an errorClass field to ActiveTurn and populate it from the event payload on turn_error before (or instead of immediately) calling endTurn. Keep the turn alive with an isError: true flag for a grace window (or until dismissed), so the badge can render "Auth error" / "Transport error" / "Timed out" instead of vanishing.

Reproduction

  1. Trigger a Codex agent turn that fails due to auth expiry (or any network/timeout failure).
  2. Observe the "Turn error" badge in the session UI.
  3. Badge disappears immediately — no actionable error class is shown.

Affected Code

  • crates/buzz-relay/src/lib.rs ~line 2785 — emit_turn_error closure, JSON payload
  • crates/buzz-relay/src/acp.rs ~line 866 — AcpError::AgentError construction (Codex auth path)
  • desktop/src/activeAgentTurnsStore.ts ~lines 342–352 — turn_error / agent_panic handling

Suggested Fix

One PR, two commits:

  1. Harness commit: expand emit_turn_error to include error_class in the JSON payload (~10 lines, no new types).
  2. Frontend commit: add errorClass/isError to ActiveTurn, read error_class from the event in the store, and render a classified error badge.

Open product question: Should the error badge stay until dismissed, auto-clear after N seconds, or clear on the next successful turn? The code for any option is trivial once decided.

Test Gaps

  • No tests for the AgentError / auth-expiry path through emit_turn_error
  • No tests for turn_error tombstone behavior in activeAgentTurnsStore
  • Tests needed for: Codex/auth AgentError, transport error, timeout paths

Out of Scope

  • New triage agent
  • Channel posting / notification policy
  • Dashboard / activity-feed redesign

References

Internal investigation by Ferret & Lazy Joe in channel #bugfix-codex-logged-out.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions