Agent pane: an unreachable server's error card leads with Retry, never Switch model

Merged
TE
3d ago
+126-3

Summary

Leo, 2026-11-03, on a cmux NEXT agent pane: "this ui is pretty awkward looking".

One item from that screenshot: a turn whose model server could not be reached (a transport, network or API connection failure) showed an error card that led with a filled Switch model. That happened whenever the card had no Retry of its own: an earlier turn, or a turn that a retry had already gone on from. Switching model is not the fix for a server that can't be reached.

  • TurnRows.tsx TurnFailure: an unreachable failure never makes Switch model the filled lead. If the chat has a Retry, Retry leads (filled). It runs acpmux's turn retry, the same path whose attempts #34306's live line counts ("Can't reach the model · retrying (N of M)"). Retry stays disabled while the request is in flight, and Switch model follows it, outlined. If there is no Retry, Switch model stays on the card, outlined.
  • Failures about the model or its route (a refused key, a proxy) keep Switch model as the filled lead with Retry after it. Capacity, sign-in, compact, access and CodeRouter cards are unchanged. No action is removed anywhere.
  • No new strings. The button classes are unchanged, so each button's hover highlight still covers only that button.
  • I left the open #34443 (the "Retrying…" note under the card's actions) alone. This diff doesn't touch that state or its strings.

Gallery: agent-pane.turn-error-card, new variants

  • server-unreachable: Retry first (filled), Switch model after it
  • server-unreachable-retrying: the retried attempt's live line says "Can't reach the model · retrying (2 of 10) · 503 server error", and the earlier card keeps Switch model outlined
  • model-error: a refused API key, with Switch model leading and Retry after it

Testing

webviews/test/turn-error-card.test.tsx gets four new tests that check which action leads for each error kind:

  • unreachable with Retry: ["Retry", "Switch model"], Retry filled
  • unreachable with no Retry: ["Switch model"], outlined. This one fails on base, where Switch model is filled.
  • unreachable through acpmux's turn retry: Retry calls retryFailed with the row and prompt, and stays disabled until it settles
  • invalid key: ["Switch model", "Retry"], Switch model filled

Ran on the fleet (cmux-ci test, Linux):

  • vp fmt on the changed files, with its change applied
  • bun test on turn-error-card, turn-error-continue, failureCopy, access-offer, signed-out-harness and cloud-signin-card: 87 pass, 0 fail
  • bun run typecheck and bun run lint: the one error in each is Cannot find name 'after' in test/transcript-spacing-kinds.test.ts:247. That is a base red from #34370, not a file this PR touches. These files report no errors.

Observed / Not run

  • Observed: the fleet results above.
  • Not run: a DEV build against a real failing gateway, and gallery captures.

Risk: low. One condition in one component, and it only changes which button gets the filled style and the lead slot for unreachable cards that have no Retry.

Changelog

Fixed: An agent chat whose model server can't be reached no longer leads its error card with Switch model; Retry leads, and Switch model follows it.

Proof

No capture: webview UI. See gallery agent-pane.turn-error-card (server-unreachable, server-unreachable-retrying, model-error).

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

Fixes the agent pane error card so an unreachable model server leads with Retry instead of Switch model. Previously, a connection failure without its own Retry showed a filled Switch model, which doesn't fix a server that can't be reached.

Bug Fixes

  • Unreachable failures now lead with filled Retry when the chat has one; Switch model follows outlined, and stays outlined when there's no Retry.
  • Failures about the model or its route (refused key, proxy) keep Switch model as the filled lead with Retry after it.
  • Adds four tests per error kind and gallery variants, with no actions removed and no new strings.