An Empty Model Handshake Needs a Recoverable Error, Not a Disabled Composer
Description and use
Problem: the chat UI disables the composer after sending a prompt when the backend returns an empty successful response, leaving the user unable to retry. Fix: replace boolean flags with a discriminated union ChatPhase and a timer that detects the first byte or silence, enabling the UI to show an error message and retry button.
Problem
The chat UI locks the composer after sending a prompt when the backend returns an empty successful response, leaving the user unable to retry or continue.
Outcome
Implemented a typed state machine for chat phases, enabling the UI to correctly handle empty successful handshakes by showing an error message and retry option instead of locking the composer.
Workflow
- 1. User types prompt and presses Enter. 2. UI sets streaming=true, disables composer, shows spinner. 3. Backend returns 200 with no data frames. 4. Reader loop exits cleanly; streaming=false, error=null. 5. UI incorrectly treats this as success, leaving composer disabled and retry hidden. 6. User cannot retry or type again. 7. Fix: replace booleans with a discriminated union ChatPhase. 8. Add timer to detect first byte or silence. 9. On empty handshake, set phase to "empty", enable composer, show status and Retry button. 10. User can retry or type new prompt.
Evidence
The composer used a boolean latch instead of a typed phase. Booleans cannot represent “handshake started but no first token.”
I replaced the booleans with a discriminated union and a timer that owns the first byte, not the full generation.
- Record date: 2026-09-21T23:00:42.369Z