The Retry Keystroke Failed After Cancel: A Dead AbortSignal Retrospective
说明与用途
键盘用户可以取消流式请求,然后按 Retry,但 Retry 按钮仍然启用,而底层 AbortController 已被取消,导致无法启动新请求,UI 处于死状态。解决方案是在每次重试时创建新的 AbortController。
问题
Keyboard users can cancel a streaming request and then press Retry, but the retry button remains enabled while the underlying AbortController is already aborted, preventing a new request from starting and leaving the UI in a dead state.
结果
The bug was caused by reusing the same AbortController across attempts. The fix is to create a new AbortController for each retry, ensuring the controller is not already aborted when starting a new fetch.
工作流
- 1. Focus composer and submit prompt with Enter.
- 2. While status says generating, press Escape to cancel.
- 3. Observe focus moves to Stop (or hidden).
- 4. Tab to Retry and press Enter.
- 5. Verify that a new attempt starts, live region announces retry, composer is enabled, and focus lands on Stop or composer.
证据
Retry is not a click handler glued onto a cancelled bubble. Retry is a transition that must create a new attempt.
The first attempt had aborted, but Retry still held the same AbortController. signal.aborted was already true, so the next fetch never started.
- 记录日期: 2026-09-07T22:14:24.696Z