Agent pane: a turn whose model can't be reached says so, not Thinking (acpmux turn_retrying)

Merged
TE
2d ago
+721-8

Summary

Leo, 2026-11-03, on a chat that read "Thinking 7m 49s": "i feel like this mac session just keeps "thinking" but idk if tool calls are somehow being hidden or if that workspace itself/session itself under acpmux is kinda weird".

The chat was not thinking. Its model gateway was down, and Claude Code was retrying the request (system/api_retry, attempt 1..10, error_status, error, about 75 s apart). acpmux logged those lines but nothing the pane reads said so, so the live line kept reading "Thinking" with a clock that counted the retries.

  • acpmux (hub/turn_retry.rs): each harness retry of a failed model request logs a mux record turn_retrying {turnId, attempt, maxRetries, error, errorStatus, retryDelayMs, source}. Sources: Claude Code's system/api_retry line and Codex's session_info_update._meta.codex.error with willRetry ("Reconnecting... 2/5", HTTP status from codexErrorInfo). It is in the transcript category, so clients get it with no new subscription. The turn is marked silent, so the model's next sign logs turn_heard, which ends the retry. Documented in the README and docs/acpmux-schema.json.
  • Agent pane: direct.ts keeps the retry on a client-only TURN_RETRY row (kind typing, drawn by nothing). turns.ts withRetry puts it on the live line, and Thinking.tsx then reads "Can't reach the model 路 retrying (7 of 10) 路 503 server error", or "The model is busy" for 429/529 and rate-limit or overload errors. It is calm: no shimmer, no clock, the cause in text ink and the count and error muted, with the error the part that truncates. It uses the same .cv-worked box, so nothing moves when it starts or ends. Once the model answers, the retries' time comes off the "Thinking" clock (before the first output, the clock times the turn from its prompt).
  • Given up: a retry that runs out still ends the turn failed, shown on the existing turn error card with Retry ("API Error: Connection error." reads as unreachable). That needed no change; the gallery shows it.
  • The iPhone app already reads claude.system.api_retry ("Retrying (2 of 10, error 503)"), so this adds no parity gap.

Gallery: agent-pane.turn-retry, with variants retrying, retrying-with-error, retrying-busy and given-up. The mock daemon has the live scene at ?mock&session=mock-model-retry (seven turn_retrying records).

Testing

  • acpmux, on fleet cmux-ci test (Linux): cargo clippy -p acpmux --all-targets -D warnings is clean. cargo test -p acpmux --lib turn_retry (3 unit tests) passes. --test claude_stream_timing passes; its new a_claude_api_retry_logs_turn_retrying_until_the_model_answers uses fake Claude's api-retry-probe. --test hub_integration -- retry silent passes 44 tests, including the Codex retry test, which now asserts turn_retrying comes before turn_heard, which comes before the redelivered answer. cargo fmt was fixed in the second commit.
  • Webviews: turnRetry.live.test.ts feeds real records through AcpmuxDirectClient over the mock socket and checks the retry row, the retry time, and the cleanup at turn end. It also covers withRetry and retryParts. test/transcript-turn-retry.test.tsx renders the gallery snapshots through turnView and Thinking.

Observed / Not run

  • Observed: the Rust results above. The webview run's results are posted as a comment.
  • Not run: a tagged DEV build against a real failing gateway.

Changelog

Fixed: A chat whose model can't be reached now says "Can't reach the model 路 retrying (N of M)" instead of "Thinking".

Proof

No capture: webview UI. See gallery agent-pane.turn-retry.

Agent session: agent session

馃 Generated with Claude Code


Autofix with [code]smith
Open in [code]smith 路 Need help on this PR? Tag @codesmith-bot with what you need.


Summary by cubic

Shows when a chat's model can't be reached instead of reading "Thinking" while the harness retries the request.

Previously a turn whose model gateway was down kept its "Thinking" live line with a clock counting the retries. Now acpmux logs each retry, and the agent pane's live line reads "Can't reach the model 路 retrying (7 of 10) 路 503 server error" (or "The model is busy" for 429/529 and rate-limit or overload errors, including status-less codes like rate_limit_error), calmly, with no sweep and no clock. When the model answers, the line returns to "Thinking" minus the time the retries took. Once a retry runs out, the turn still ends failed on the existing error card with Retry.

acpmux

  • Each harness retry of a failed model request logs a turn_retrying mux record (transcript category), sourced from Claude Code's system/api_retry and Codex's _meta.codex.error with willRetry.
  • The retry marks the turn silent, so the model's next sign logs turn_heard, ending the retry.

Agent pane

  • The live line carries a client-only TURN_RETRY row, drawn in the same .cv-worked box so nothing moves when retries start or end.
  • No new subscription needed; existing transcript clients receive the records.