Skip to content

feat(site): offer repair and retry when a chat is in an invalid state - #28749

Draft
bpmct wants to merge 1 commit into
mainfrom
bpmct/chat-invalid-state-repair-retry
Draft

feat(site): offer repair and retry when a chat is in an invalid state#28749
bpmct wants to merge 1 commit into
mainfrom
bpmct/chat-invalid-state-repair-retry

Conversation

@bpmct

@bpmct bpmct commented Aug 28, 2026

Copy link
Copy Markdown
Member

When a chat is stuck in an invalid execution state, every mutation returns a bare 409 (Chat is in an invalid state.) and the UI dead-ends on an error toast. The recovery endpoint (POST /api/v2/chats/{chat}/reconcile-invalid) exists but is not exposed anywhere in the product, so users end up in support threads (see the linked Discord case, resolved by hand-running curl).

This PR wires that recovery path into the archive and unarchive actions: when they fail with the invalid-state 409, the UI opens a "Repair agent state" dialog. Confirming reconciles the chat and retries the original action once. All other mutations keep their existing error handling.

Closes CODAGT-998.

Demo

repair and retry demo

Repair dialog Chat archived after repair
dialog archived
Full recording

End-to-end reproduction

Validated on a local ./scripts/develop.sh instance with CODER_AI_GATEWAY_ENABLED=true:

  1. Created a chat, then forced the invalid tuple via SQL (status='waiting' plus a queued message, the only unarchived invalid combination in chatstate.ClassifyExecutionState).
  2. Confirmed PATCH /api/v2/chats/{chat} with archived: true returns the 409.
  3. In the UI: Archive agent opens the repair dialog; "Repair and retry" reconciles (chat lands in error state with the standard reconcile message) and the retry archives it. Verified in the archived filter view.
Implementation notes and decisions
  • isChatInvalidStateError matches HTTP 409 plus the exact message because the backend response carries no machine-readable error code for this case; noted in a comment at the constant.
  • Detection helper and the reconcile mutation live in site/src/api/queries/chats.ts next to the archive mutations; the dialog state lives in AgentsPageLayout.tsx, which owns the chat mutations today.
  • The retry is guarded by a chat-ID ref so a second invalid-state failure falls through to the normal error toast instead of looping the dialog.
  • Scope is deliberately archive/unarchive only, where users actually hit this. A backend follow-up (adding a Detail hint to writeChatInvalidState, and possibly family-aware reconcile-on-archive) is discussed in CODAGT-998.
  • Validation: pnpm exec tsc -p . --noEmit, biome on changed files, Vitest for the helper, and Storybook interaction tests for both dialog paths (repair-and-retry, dismiss).

Opened by Coder Agents on behalf of @bpmct.

@linear-code

linear-code Bot commented Aug 28, 2026

Copy link
Copy Markdown

CODAGT-998

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant