33 / 122 · Tool use
Tool failure
One call failed. The error, the attempt count, and a retry that doesn't restart the turn.
fetchhttps://api.example.com/v1/issues1/3
ETIMEDOUT after 30000ms
Installation
npx shadcn@latest add "@assistant-ui/elements-tool-error"
Usage
import { ToolError } from "@/components/elements/tool-error";
<ToolError
name="fetch"
target="https://api.example.com/v1/issues"
message="ETIMEDOUT after 30000ms"
attempt={2}
maxAttempts={3}
retrying={false}
onRetry={retry}
/>Props
name*stringTool that failed.
target*stringWhat it was called against.
message*stringThe raw error, shown in mono rather than reworded.
attempt*numberWhich attempt this was.
maxAttempts*numberHow many the runtime will make.
retrying*booleanSwaps the retry control for a spinner and disables it.
onRetry() => voidCalled to run the call again without restarting the turn.
onSkip() => voidCalled to give up on this call and let the turn continue.
classNamestringExtra classes merged onto the root.
Source
tool-error.tsx"use client";
import { AlertCircleIcon, Loader2Icon, RotateCwIcon } from "lucide-react";
import { cn } from "@/lib/utils";
import { field, mono, paper } from "./surfaces";
export function ToolError({
name,
target,
message,
attempt,
maxAttempts,
retrying,
onRetry,
onSkip,
className,
}: {
name: string;
target: string;
message: string;
attempt: number;
maxAttempts: number;
retrying: boolean;
onRetry?: () => void;
onSkip?: () => void;
className?: string;
}) {
return (
<div
className={cn(
paper,
"flex w-full max-w-sm flex-col gap-3 rounded-2xl p-3.5",
className,
)}
>
<div className="flex items-center gap-2.5">
<AlertCircleIcon className="size-3.5 shrink-0 text-red-500" />
<span className={cn(mono, "text-foreground/55 shrink-0")}>{name}</span>
<span className="text-foreground/80 min-w-0 flex-1 truncate text-[13px]">
{target}
</span>
<span className={cn(mono, "text-foreground/30 shrink-0 tabular-nums")}>
{attempt}/{maxAttempts}
</span>
</div>
<div
className={cn(
field,
"rounded-xl px-3 py-2 font-mono text-[11px] leading-relaxed text-red-700 dark:text-red-300",
)}
>
{message}
</div>
<div className="flex items-center justify-end gap-2">
<button
type="button"
onClick={onSkip}
disabled={!onSkip}
className="text-foreground/45 hover:bg-foreground/[0.06] hover:text-foreground/90 h-7 rounded-full px-2.5 text-xs font-medium transition-[background-color,color,scale] duration-150 active:scale-[0.96] disabled:pointer-events-none disabled:opacity-30"
>
Skip
</button>
<button
type="button"
onClick={onRetry}
disabled={retrying}
className="text-foreground/70 hover:bg-foreground/[0.06] hover:text-foreground/95 flex h-7 items-center gap-1.5 rounded-full px-2.5 text-xs font-medium transition-[background-color,color,scale] duration-150 active:scale-[0.96] disabled:pointer-events-none"
>
{retrying ? (
<Loader2Icon className="size-3 animate-spin motion-reduce:animate-none" />
) : (
<RotateCwIcon className="size-3" />
)}
{retrying ? "Retrying" : "Retry"}
</button>
</div>
</div>
);
}