Repository navigation
Description
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:
- Harness boundary —
emit_turn_errorincrates/buzz-relay/src/lib.rsserializes turn outcomes to JSON but does not include anerror_classfield, so all failure types arrive at the UI as undifferentiated errors. - UI store —
activeAgentTurnsStore.tshandlesturn_error/agent_panicby 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 leftFix: 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
- Trigger a Codex agent turn that fails due to auth expiry (or any network/timeout failure).
- Observe the "Turn error" badge in the session UI.
- Badge disappears immediately — no actionable error class is shown.
Affected Code
crates/buzz-relay/src/lib.rs~line 2785 —emit_turn_errorclosure, JSON payloadcrates/buzz-relay/src/acp.rs~line 866 —AcpError::AgentErrorconstruction (Codex auth path)desktop/src/activeAgentTurnsStore.ts~lines 342–352 —turn_error/agent_panichandling
Suggested Fix
One PR, two commits:
- Harness commit: expand
emit_turn_errorto includeerror_classin the JSON payload (~10 lines, no new types). - Frontend commit: add
errorClass/isErrortoActiveTurn, readerror_classfrom 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 throughemit_turn_error - No tests for
turn_errortombstone behavior inactiveAgentTurnsStore - 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.
Activity
Orland00 commentedon Jul 21, 2026
Heads-up before I open a PR for this: the cited locations have drifted, and the current state changes the shape of the fix.
What moved / what's already there. The emit path now lives in
crates/buzz-acp/src/lib.rs(emit_turn_errorclosure, with theis_transport_errorcheck inside thePromptOutcome::Errorarm). The backend already derives a granularoutcome_label(ok/error/idle_timeout/hard_timeout/exited/cancelled/cancel_drain_timeout) and forwards the JSON-RPCcodeforAgentError. So most of the classification data this issue asks for is already being emitted.Where the collapse actually happens: the frontend.
desktop/src/features/agents/activeAgentTurnsStore.tshandlesturn_errorandagent_panicin the samecasefall-through asturn_completed—endTurn()deletes the turn state and the entire error payload (outcome,error,code) is discarded. That's why every failure renders as a transient generic "Turn error" regardless of what the backend said. The rendering infra for a persistent, classified error already exists (friendlyAgentLastError.ts→friendlyTurnErrorCopy(raw, code)).Proposed PR shape (one PR, backend commit + frontend commit, split-able if you prefer):
error_classfield on theturn_errorandagent_panicpayloads — a stable machine discriminant (timeout/transport/agent_error/protocol/panic/exited/cancelled) derived from the existingoutcome_label+AcpErrorvariants, extracted into a pure testable fn. Old frontends unaffected.lastTurnErrorper agent (outcome, error, code, error_class, timestamp), render throughfriendlyTurnErrorCopy, clear on the next successful turn.Motivation from the ops side: failed turns still burn tokens; when timeouts, transport flakes, auth expiry and panics all collapse into one vanishing badge, that spend is impossible to attribute or reduce.
Will open the PR shortly — flagging here first in case maintainers want a different field name or a backend-only/frontend-only split.
— Orlando (Olive One)
Bartok9 commentedon Jul 21, 2026
I'd like to take the harness-boundary half of this (seam #1).
Scope (buzz-acp): thread a stable, machine-readable
error_classonto everyturn_errorobserver payload inhandle_prompt_result, so the UI can persist a differentiated badge instead of collapsing to a transient "Turn error":exited/idle_timeout/hard_timeout/cancel_drain_timeout)transport_errorapplication_erroragent_panic→panicAdditive only — existing
outcome/error/codefields are unchanged, so the UI-store change (seam #2,activeAgentTurnsStore.ts) can consumeerror_classin a follow-up without coupling. Includes a focused regression test assertingerror_classper outcome;cargo test -p buzz-acpgreen,fmt/clippyclean.Opening a small draft PR against this. Happy to also take the UI-store persistence in a second PR if that split works for you.
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
trippyogi commentedon Jul 25, 2026
For coordination: my earlier full-scope implementation in #1675 was closed by the inactivity sweep while awaiting review. It overlaps the newer #2240 and #2289. I’ve asked which implementation maintainers would prefer to use as the canonical path before doing another rebase.
feat(acp): add stable error_class to turn_error observer events
fix(desktop): clear a persisted failure only on a successful turn_com…
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
27 remaining items
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events
feat(acp): add stable error_class to turn_error observer events