errors
Partial Response
A cut-short assistant reply with Continue and Retry actions.
The ai-patterns skill gives any Claude Code session access to the full pattern library. Once installed, you can reference patterns by name in your prompts — for example, "use the Streaming Text pattern for the assistant reply" or "wire up the Tool Approval pattern before each shell command". The skill exposes each pattern's UX spec, component source, and demo so Claude can
Stopped · Response was stopped before it finished.
"use client";
import * as React from "react";
import { motion } from "motion/react";
import { ChevronRight, RotateCcw } from "lucide-react";
import { cn } from "@/lib/utils";
export type PartialResponseReason = "interrupted" | "max-tokens" | "error";
export interface PartialResponseProps {
content: string;
reason?: PartialResponseReason;
onResume?: () => void;
onRetry?: () => void;
resuming?: boolean;
className?: string;
}
const reasonConfig: Record<PartialResponseReason, { label: string; hint: string; showResume: boolean }> = {
interrupted: {
label: "Stopped",
hint: "Response was stopped before it finished.",
showResume: true,
},
"max-tokens": {
label: "Cut off",
hint: "Response reached the output limit.",
showResume: true,
},
error: {
label: "Incomplete",
hint: "Response stopped due to an error.",
showResume: false,
},
};
export function PartialResponse({
content,
reason = "interrupted",
onResume,
onRetry,
resuming = false,
className,
}: PartialResponseProps) {
const config = reasonConfig[reason];
return (
<div className={cn("flex flex-col gap-0 rounded-2xl border border-border bg-muted/30 overflow-hidden", className)}>
<div className="px-4 py-3 text-sm leading-relaxed text-foreground">
{content}
<motion.span
className="ml-0.5 inline-block h-[1em] w-0.5 translate-y-[1px] rounded-sm bg-foreground/40"
animate={{ opacity: [1, 0] }}
transition={{ duration: 0.8, repeat: Infinity, ease: "linear" }}
aria-hidden
/>
</div>
<div className="flex items-center justify-between border-t border-border/60 bg-muted/40 px-4 py-2.5">
<p className="text-xs text-muted-foreground">
<span className="font-medium text-foreground">{config.label}</span>
{" · "}
{config.hint}
</p>
<div className="flex items-center gap-1.5">
{onRetry && (
<button
type="button"
onClick={onRetry}
aria-label="Retry from scratch"
className="flex items-center gap-1 rounded-full px-2.5 py-1 text-xs text-muted-foreground transition-colors hover:bg-accent hover:text-foreground"
>
<RotateCcw className="size-3" aria-hidden />
Retry
</button>
)}
{config.showResume && onResume && (
<button
type="button"
onClick={onResume}
disabled={resuming}
aria-label={resuming ? "Resuming response" : "Continue response"}
className="flex items-center gap-1 rounded-full bg-foreground px-2.5 py-1 text-xs font-medium text-background transition-opacity hover:opacity-80 disabled:opacity-50"
>
{resuming ? (
"Continuing…"
) : (
<>
Continue
<ChevronRight className="size-3" aria-hidden />
</>
)}
</button>
)}
</div>
</div>
</div>
);
}
A UX spec for this pattern — written for agents implementing or reusing it, not the code.
# Partial Response
## Summary
A message component for responses that ended before completion. Displays whatever text was generated, clearly marks it as incomplete, and offers Continue (resume from the cut-off point) and Retry (regenerate from scratch) actions.
## When to use
- The stream ended before a natural conclusion — stopped by the user, cut off by an output token limit, or dropped by a transient error — and some content was produced.
- Any time preserving and resuming the partial output is preferable to discarding it. A partial response with continuation is nearly always more useful than a blank error card.
- In the message thread at the position of the incomplete assistant turn, not as a modal or a separate panel.
## When not to use
- Zero content produced — if the response never started, use the Generation Error pattern instead.
- The content is corrupted or nonsensical (e.g. a mid-token cut-off in a code block) — in this case retry is the better default action; hide Continue.
- Read-only transcript views where resuming is not an option.
## Reasons
Three distinct reasons change the copy and available actions:
- **interrupted**: User clicked Stop. Label "Stopped". Both Continue and Retry are available.
- **max-tokens**: Hit the model's output token ceiling. Label "Cut off". Both Continue (model continues from the cut-off) and Retry are available.
- **error**: Transient error mid-stream. Label "Incomplete". Only Retry is available — continuing from an error state may reproduce the error.
## Anatomy
- Content area: the partial text, rendered as-is, with a blinking cursor appended to signal incompleteness.
- Status bar: a hairline-bordered footer row.
- Left: reason label (bold) + separator + one-line hint in muted text.
- Right: Retry button (ghost, muted) and Continue button (filled, primary). Continue is the primary action.
- Blinking cursor: a narrow vertical bar animating opacity 1→0 on a 0.8s loop. Hidden with `aria-hidden`.
## Behavior
- The cursor blinks continuously at rest to keep the "mid-stream" sense alive even after the stream stopped.
- Tapping Continue puts the button into "Continuing…" disabled state immediately; the caller owns the actual resumption request and passes `resuming={true}` back.
- Resumption semantics (sending the prior context + the partial response as the new prompt) are the caller's responsibility. This component signals intent; it does not issue API calls.
- Tapping Retry discards the partial content and issues a fresh generation from the original prompt. The caller handles this by removing the partial message and resubmitting.
- Only one action may be in-flight at a time (Continue disables during `resuming`). Retry does not need a loading state if the caller immediately replaces this component with a new streaming response.
## Content guidelines
- Reason labels: "Stopped", "Cut off", "Incomplete" — short, factual, no punctuation.
- Hint text: one sentence, no more. "Response was stopped before it finished." / "Response reached the output limit." / "Response stopped due to an error."
- Continue label: "Continue" with a › chevron — implies the stream will pick up from here, not start over.
- Retry label: "Retry" — unambiguous, separate from Continue.
- Never expose the token count or technical limit to the user ("stopped at 4096 tokens").
## Accessibility
- The blinking cursor has `aria-hidden` — it is a decorative animation.
- Continue button `aria-label` changes to "Resuming response" when `resuming` is true, giving screen-reader users feedback without a visible spinner.
- The status bar text is available to assistive technology as static text; no additional live region is needed since the component appears fully rendered.
- Keyboard: both actions are reachable via Tab; Continue is the last focused element in the component (rightmost in the footer), matching its primary-action status.
## Related patterns
- Generation Error — for requests that produced no output at all.
- Rate Limit — for quota-exceeded states where the user must wait before retrying.
- Stop Generation Button — the control that triggers the "interrupted" reason for this pattern.
- Streaming Text — the in-progress version of this component, before the stream ends.