Stop generation button is present during streaming
Why it matters
A streaming AI response can run 10–30 seconds. Without a stop control wired to AbortController, users who trigger an unwanted response — wrong prompt, sensitive topic, runaway context — have no recourse except a full page reload, which destroys conversation history. This directly violates iso-25010:2011 reliability.fault-tolerance: the system must handle user-initiated cancellation gracefully. Beyond UX, holding an open streaming connection for a generation the user abandoned wastes API credits and server resources. On metered plans this creates real cost exposure.
Severity rationale
Critical because the user has no escape from an unwanted or runaway generation except destroying the page — a hard blocker for any conversational AI product.
Remediation
Wire useChat's stop() or an AbortController to a button that renders only while isLoading is true. The control must be in the DOM and visible during streaming — dead code in JSX does not satisfy this check.
const { stop, isLoading } = useChat()
<button
onClick={stop}
aria-label="Stop generating"
className={isLoading ? 'flex items-center gap-1' : 'hidden'}
>
<SquareIcon className="w-4 h-4" /> Stop
</button>
For raw fetch implementations, store an AbortController in a ref and call .abort() from the stop handler in src/components/chat/ChatInput.tsx.
Detection
-
ID:
stop-generation -
Severity:
critical -
What to look for: Count all streaming state variables (
isLoading,isStreaming,isPending) across the codebase. For each, enumerate whether a stop, cancel, or abort control is conditionally rendered. Check forAbortControllerusage in API calls orstop()function calls from AI SDK hooks. Verify the stop control replaces or supplements the submit button while generation is in progress. A stop button that exists in JSX but is never conditionally shown during streaming does not count as pass — do not pass based on dead code. -
Pass criteria: A stop or cancel button is visible while the AI is generating a response. The button calls
AbortController.abort()or an equivalent cancellation mechanism, ending the stream. At least 1 streaming entry point must have a wired stop control. Report: "X of Y streaming entry points have stop controls." -
Fail criteria: No stop button or cancel control found. The user must wait for generation to complete or refresh the page to cancel a long-running or unwanted response.
-
Skip (N/A) when: Same as
regeneration-button— project has no AI generation interface. -
Detail on fail:
"No stop/cancel button found — streaming state detected (isLoading flag in useChat or similar) but no AbortController or stop() handler wired to a UI control". -
Remediation: Long AI responses can take 10-30 seconds. Without a stop button, users who realize the response is wrong have no recourse.
const { stop, isLoading } = useChat() <button onClick={stop} aria-label="Stop generating" className={isLoading ? 'block' : 'hidden'} > <SquareIcon className="w-4 h-4" /> Stop </button>With a manual fetch approach:
const abortControllerRef = useRef<AbortController | null>(null) const startGeneration = () => { abortControllerRef.current = new AbortController() fetch('/api/chat', { signal: abortControllerRef.current.signal, ... }) } const stopGeneration = () => abortControllerRef.current?.abort()
External references
- iso-25010:2011 · reliability.fault-tolerance — Fault Tolerance — ability to operate as intended despite faults (stream cancellation as a reliability control)
Taxons
History
- 2026-04-18·v1.0.0·Initial import from ai-ux-patterns·automated