Skip to content
AI Interfaces block

Failed Run Trace

The trace after something broke: retries counted in the open with what each one cost, a step that was silently worked around drawn in amber rather than green, and the raw error kept apart from the agent’s reading of it.

337 lineslucide-reactAdded 27 Aug 2026Updated 2 Sept 2026
  • agent
  • error
  • retry
  • trace
  • recovery

What's included

  • components/agent-run-failure.tsx
  • Needs lucide-react

Works with

  • React
  • Next.js
  • Tailwind CSS
  • TypeScript

npx hoverlab add agent-run-failure

Or over MCP, from your editor's agent — no account needed.

License

Free to read, copy and install, for personal and non-commercial projects. Shipping it in client work or a paid product needs Pro ($79 once). The source lands in your repo and stops being ours — no attribution, nothing to upgrade.

Was this useful?

Start a page with this section — add more, order them, and leave with the page source.

Preview

Deploy api-gateway to staging

Paused on step 3 of 5 · 211s · $0.14 spent so far

  1. Read the deploy manifest — Done

    infra/deploy.yaml, 240 lines

  2. Resolve the image tag — Recovered

    Registry timed out; used the digest pinned in the lockfile instead.

    Recovered — what the later steps used came from the fallback, not from the registry.

  3. Apply the migration — Failed

    Blocked. The migration wants a lock the reporting job is holding.

    • Attempt 1Lock not available after 60s60s · $0.04
    • Attempt 2Lock not available after 60s60s · $0.04
    • Attempt 3Lock not available after 60s60s · $0.04
  4. Restart the workers — Waiting

    Not started — blocked by the step above.

  5. Verify the health check — Waiting

    Not started.

Rendered live in your current theme — this is the same component whose source is below, not a screenshot of it.

Source

components/agent-run-failure.tsx
'use client'

/**
 * <AgentRunFailure> — the step that failed, and what the agent did about it.
 *
 * Agent Reasoning had the four surfaces of a run that is going well: the
 * plan, the thinking, the tool calls and the working indicator. Every one
 * of them is drawn for the happy path. This is the same trace after
 * something broke, which is the state people actually stare at.
 *
 * THE ONE THING THIS GETS RIGHT THAT MOST DO NOT
 *
 * A step that failed and was worked around is drawn differently from a
 * step that succeeded — not the same green tick. Silent recovery is the
 * more dangerous of the two failures, because the run reports success and
 * the person never learns that the deploy went out against a stale cache
 * or that the "found" record was actually created. Amber, with the words
 * "recovered — the result below came from the fallback".
 *
 * RETRIES ARE COUNTED IN THE OPEN
 *
 * Attempts are stacked under the step with what changed each time and what
 * each one cost. An agent quietly retrying a 30-second tool call six times
 * is the single most common way a run becomes expensive, and a spinner
 * that says "working…" hides exactly that. The running total is in the
 * header where the stop button is, because the number is the reason
 * somebody presses it.
 *
 * THE ERROR IS THE REAL ERROR
 *
 * Verbatim, in monospace, copyable — not "something went wrong". The
 * person reading this is usually the only one who can tell whether the
 * agent's diagnosis is right, and they cannot do that from a paraphrase.
 * The agent's reading of it is shown *next to* the raw text and labelled
 * as an interpretation, so the two never get confused.
 *
 * THREE ROUTES OUT, AND NONE OF THEM IS "OK"
 *
 * Retry unchanged, retry with an edit, or take it over. A dialog whose
 * only button dismisses it leaves the run wedged, which is how agent
 * products end up with a support queue full of "it just stopped".
 *
 * ACCESSIBILITY: the trace is an ordered list, so position and length are
 * announced. Status is carried by an icon with a `sr-only` word as well as
 * colour. The failure panel is `role="alert"` — it appears mid-run without
 * a focus change, and is the one thing here that must interrupt.
 */

import * as React from 'react'
import {
  AlertTriangle,
  Check,
  ChevronDown,
  Copy,
  CornerDownLeft,
  Loader2,
  RefreshCw,
  UserRoundCog,
  X,
} from 'lucide-react'

export type StepState = 'done' | 'recovered' | 'failed' | 'waiting' | 'running'

export interface RunAttempt {
  label: string
  outcome: string
  seconds: number
  cents: number
}

export interface RunStep {
  title: string
  detail: string
  state: StepState
  /** Raw tool output for a failed step — verbatim, never paraphrased. */
  rawError?: string
  /** The agent's reading of that output, kept visibly separate from it. */
  diagnosis?: string
  attempts?: RunAttempt[]
}

export interface AgentRunFailureProps {
  runName?: string
  steps?: RunStep[]
  className?: string
}

const DEFAULT_STEPS: RunStep[] = [
  {
    title: 'Read the deploy manifest',
    detail: 'infra/deploy.yaml, 240 lines',
    state: 'done',
  },
  {
    title: 'Resolve the image tag',
    detail: 'Registry timed out; used the digest pinned in the lockfile instead.',
    state: 'recovered',
    attempts: [
      { label: 'Attempt 1', outcome: 'registry.internal timed out after 30s', seconds: 30, cents: 0 },
      { label: 'Attempt 2', outcome: 'Fell back to the lockfile digest', seconds: 1, cents: 2 },
    ],
  },
  {
    title: 'Apply the migration',
    detail: 'Blocked. The migration wants a lock the reporting job is holding.',
    state: 'failed',
    rawError:
      'ERROR: could not obtain lock on relation "orders" (SQLSTATE 55P03)\n  detail: process 4417 holds AccessExclusiveLock\n  hint: retrying will queue behind the same lock',
    diagnosis:
      'The nightly reporting job took an exclusive lock on orders at 02:00 and has not released it. Retrying as-is will queue behind the same lock and time out again. Either wait for that job, or run the migration with a lock timeout and accept a partial apply.',
    attempts: [
      { label: 'Attempt 1', outcome: 'Lock not available after 60s', seconds: 60, cents: 4 },
      { label: 'Attempt 2', outcome: 'Lock not available after 60s', seconds: 60, cents: 4 },
      { label: 'Attempt 3', outcome: 'Lock not available after 60s', seconds: 60, cents: 4 },
    ],
  },
  { title: 'Restart the workers', detail: 'Not started — blocked by the step above.', state: 'waiting' },
  { title: 'Verify the health check', detail: 'Not started.', state: 'waiting' },
]

const STATE_STYLE: Record<StepState, { word: string; dot: string; text: string }> = {
  done: { word: 'Done', dot: 'bg-emerald-500', text: 'text-foreground' },
  recovered: { word: 'Recovered', dot: 'bg-amber-500', text: 'text-foreground' },
  failed: { word: 'Failed', dot: 'bg-destructive', text: 'text-foreground' },
  running: { word: 'Running', dot: 'bg-primary', text: 'text-foreground' },
  waiting: { word: 'Waiting', dot: 'bg-border', text: 'text-muted-foreground' },
}

function money(cents: number) {
  return `$${(cents / 100).toFixed(2)}`
}

export function AgentRunFailure({
  runName = 'Deploy api-gateway to staging',
  steps = DEFAULT_STEPS,
  className = '',
}: AgentRunFailureProps) {
  /*
    Per-instance namespace for every id below. The literal roots these
    replaced were the same string in every instance, so two copies of this
    block on one page pointed every label at the first copy's control.
  */
  const uid = React.useId()

  const [openAttempts, setOpenAttempts] = React.useState<number | null>(2)
  const [copied, setCopied] = React.useState(false)

  /* The number that is the reason somebody reaches for the stop button. */
  const totals = steps
    .flatMap((s) => s.attempts ?? [])
    .reduce(
      (acc, a) => ({ seconds: acc.seconds + a.seconds, cents: acc.cents + a.cents }),
      { seconds: 0, cents: 0 },
    )
  const failedIndex = steps.findIndex((s) => s.state === 'failed')
  const failed = failedIndex >= 0 ? steps[failedIndex] : undefined

  return (
    <section className={`mx-auto w-full max-w-2xl px-4 py-16 sm:px-6 ${className}`}>
      <div className="overflow-hidden rounded-2xl border border-border bg-card">
        <header className="flex flex-wrap items-center gap-3 border-b border-border px-5 py-4">
          <div className="min-w-0 flex-1">
            <h2 className="truncate text-sm font-semibold text-foreground">{runName}</h2>
            <p className="mt-0.5 text-xs text-muted-foreground">
              Paused on step {failedIndex + 1} of {steps.length} ·{' '}
              <span className="tabular-nums">
                {Math.round(totals.seconds)}s · {money(totals.cents)}
              </span>{' '}
              spent so far
            </p>
          </div>
          <button
            type="button"
            className="inline-flex h-8 items-center gap-1.5 rounded-lg border border-border bg-background px-3 text-xs font-medium text-foreground transition hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
          >
            <X aria-hidden className="h-3.5 w-3.5" />
            Stop run
          </button>
        </header>

        <ol className="divide-y divide-border">
          {steps.map((step, i) => {
            const style = STATE_STYLE[step.state]
            const isOpen = openAttempts === i
            return (
              <li key={step.title} className="px-5 py-4">
                <div className="flex gap-3">
                  <span className="mt-1.5 flex h-4 w-4 shrink-0 items-center justify-center">
                    {step.state === 'running' ? (
                      <Loader2
                        aria-hidden
                        className="h-3.5 w-3.5 text-primary motion-safe:animate-spin"
                      />
                    ) : (
                      <span aria-hidden className={`h-2 w-2 rounded-full border border-transparent ${style.dot}`} />
                    )}
                  </span>
                  <div className="min-w-0 flex-1">
                    <p className={`text-sm font-medium ${style.text}`}>
                      {step.title}
                      {/* Status in words as well as colour. */}
                      <span className="sr-only"> — {style.word}</span>
                    </p>
                    <p className="mt-0.5 text-xs text-muted-foreground">{step.detail}</p>

                    {step.state === 'recovered' ? (
                      <p className="mt-2 inline-flex items-start gap-1.5 rounded-lg bg-amber-500/10 px-2 py-1 text-xs text-amber-700 dark:text-amber-400">
                        <AlertTriangle aria-hidden className="mt-0.5 h-3 w-3 shrink-0" />
                        Recovered — what the later steps used came from the
                        fallback, not from the registry.
                      </p>
                    ) : null}

                    {step.attempts && step.attempts.length > 1 ? (
                      <>
                        <button
                          type="button"
                          onClick={() => setOpenAttempts(isOpen ? null : i)}
                          aria-expanded={isOpen}
                          /* Paired with aria-controls — expanded on its own
                             says a thing opened but not which thing. */
                          aria-controls={`${uid}-attempts-${i}`}
                          className="mt-2 inline-flex items-center gap-1 rounded-md text-xs text-muted-foreground transition hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                        >
                          <ChevronDown
                            aria-hidden
                            className={`h-3 w-3 transition-transform ${isOpen ? 'rotate-180' : ''}`}
                          />
                          {step.attempts.length} attempts
                        </button>
                        {isOpen ? (
                          <ul
                            id={`${uid}-attempts-${i}`}
                            className="mt-2 space-y-1 border-s border-border ps-3"
                          >
                            {step.attempts.map((a) => (
                              <li
                                key={a.label}
                                className="flex flex-wrap items-baseline gap-x-2 text-xs text-muted-foreground"
                              >
                                <span className="font-medium text-foreground">{a.label}</span>
                                <span className="min-w-0 flex-1">{a.outcome}</span>
                                <span className="tabular-nums">
                                  {a.seconds}s · {money(a.cents)}
                                </span>
                              </li>
                            ))}
                          </ul>
                        ) : null}
                      </>
                    ) : null}
                  </div>
                </div>
              </li>
            )
          })}
        </ol>

        {failed ? (
          /* role="alert": this arrives mid-run without a focus change. */
          <div role="alert" className="border-t border-border bg-destructive/5 px-5 py-4">
            <h3 className="flex items-center gap-2 text-sm font-semibold text-foreground">
              <AlertTriangle aria-hidden className="h-4 w-4 text-destructive" />
              {failed.title} could not complete
            </h3>

            {failed.rawError ? (
              <div className="mt-3">
                <div className="flex items-center justify-between">
                  <p className="text-xs font-medium text-foreground">
                    What the tool returned
                  </p>
                  <button
                    type="button"
                    onClick={() => {
                      void navigator.clipboard?.writeText(failed.rawError ?? '')
                      setCopied(true)
                      window.setTimeout(() => setCopied(false), 2000)
                    }}
                    className="inline-flex h-7 items-center gap-1.5 rounded-lg px-2 text-xs text-muted-foreground transition hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"
                  >
                    {copied ? (
                      <Check aria-hidden className="h-3.5 w-3.5" />
                    ) : (
                      <Copy aria-hidden className="h-3.5 w-3.5" />
                    )}
                    {copied ? 'Copied' : 'Copy'}
                  </button>
                </div>
                <pre className="mt-1 overflow-x-auto rounded-lg border border-border bg-background px-3 py-2 font-mono text-xs leading-relaxed text-foreground">
                  <code>{failed.rawError}</code>
                </pre>
              </div>
            ) : null}

            {failed.diagnosis ? (
              <div className="mt-3">
                {/* Labelled as a reading of the error, never merged with it. */}
                <p className="text-xs font-medium text-foreground">
                  What the agent thinks that means
                </p>
                <p className="mt-1 text-xs leading-relaxed text-muted-foreground">
                  {failed.diagnosis}
                </p>
              </div>
            ) : null}

            <div className="mt-4 flex flex-wrap gap-2">
              <button
                type="button"
                className="inline-flex h-9 items-center gap-1.5 rounded-lg bg-primary px-3 text-sm font-semibold text-primary-foreground transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
              >
                <CornerDownLeft aria-hidden className="h-4 w-4" />
                Retry with a lock timeout
              </button>
              <button
                type="button"
                className="inline-flex h-9 items-center gap-1.5 rounded-lg border border-border bg-background px-3 text-sm font-medium text-foreground transition hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
              >
                <RefreshCw aria-hidden className="h-4 w-4" />
                Retry unchanged
              </button>
              <button
                type="button"
                className="inline-flex h-9 items-center gap-1.5 rounded-lg border border-border bg-background px-3 text-sm font-medium text-foreground transition hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"
              >
                <UserRoundCog aria-hidden className="h-4 w-4" />
                I&rsquo;ll take it from here
              </button>
            </div>
          </div>
        ) : null}
      </div>
    </section>
  )
}

Before you paste

  • Styling is Tailwind utility classes on semantic tokens (bg-card, text-muted-foreground) — it inherits your theme instead of overriding it.
  • Install: npm i lucide-react
  • Every prop has a default, so it renders standalone before you wire it up.

Where it goes

Drop it at components/agent-run-failure.tsx and import it where you need the section:

import { AgentRunFailure } from '@/components/agent-run-failure'

Customize

1 of this block’s props are simple enough to drive from here. Change them and the block below re-renders — it is the same component whose source is above, not a mock of it. Everything else it accepts is in the table underneath.

Props

Read out of the component’s own type and signature, so this cannot drift from the source below. Every prop has a default — the component renders standalone before you pass it anything.

PropTypeDefault
runNamestring'Deploy api-gateway to staging'
stepsRunStep[]DEFAULT_STEPS
classNamestring''

Not using React?

The same block rendered once to markup, wrapped as a file your framework compiles. Tailwind classes are framework-agnostic, so the design transfers intact — the behaviour does not.

This block is interactive. The markup below is its initial state with the event handlers stripped — you will need to re-wire the behaviour in your framework.

agent-run-failure.html
<!--
  Failed Run Trace — markup from the Hoverlab catalog.

  This is the block rendered once to HTML and wrapped as a component
  file. It is not a port of the React source: the Tailwind classes carry
  the design, which is the part that took the work, and they are the same
  in every framework.

  This block is interactive in React and the handlers are NOT here.
  Buttons, toggles and menus render in their initial state and do
  nothing until you wire them up.
-->
<section class="mx-auto w-full max-w-2xl px-4 py-16 sm:px-6 ">
  <div class="overflow-hidden rounded-2xl border border-border bg-card">
    <header class="flex flex-wrap items-center gap-3 border-b border-border px-5 py-4">
      <div class="min-w-0 flex-1">
        <h2 class="truncate text-sm font-semibold text-foreground">Deploy api-gateway to staging</h2>
        <p class="mt-0.5 text-xs text-muted-foreground">Paused on step 3 of 5 · <span class="tabular-nums">211s · $0.14</span> spent so far</p>
      </div>
      <button type="button" class="inline-flex h-8 items-center gap-1.5 rounded-lg border border-border bg-background px-3 text-xs font-medium text-foreground transition hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-x h-3.5 w-3.5" aria-hidden="true"><path d="M18 6 6 18"></path><path d="m6 6 12 12"></path></svg>Stop run</button>
    </header>
    <ol class="divide-y divide-border">
      <li class="px-5 py-4">
        <div class="flex gap-3">
          <span class="mt-1.5 flex h-4 w-4 shrink-0 items-center justify-center">
            <span aria-hidden="true" class="h-2 w-2 rounded-full border border-transparent bg-emerald-500"></span>
          </span>
          <div class="min-w-0 flex-1">
            <p class="text-sm font-medium text-foreground">Read the deploy manifest<span class="sr-only"> — Done</span></p>
            <p class="mt-0.5 text-xs text-muted-foreground">infra/deploy.yaml, 240 lines</p>
          </div>
        </div>
      </li>
      <li class="px-5 py-4">
        <div class="flex gap-3">
          <span class="mt-1.5 flex h-4 w-4 shrink-0 items-center justify-center">
            <span aria-hidden="true" class="h-2 w-2 rounded-full border border-transparent bg-amber-500"></span>
          </span>
          <div class="min-w-0 flex-1">
            <p class="text-sm font-medium text-foreground">Resolve the image tag<span class="sr-only"> — Recovered</span></p>
            <p class="mt-0.5 text-xs text-muted-foreground">Registry timed out; used the digest pinned in the lockfile instead.</p>
            <p class="mt-2 inline-flex items-start gap-1.5 rounded-lg bg-amber-500/10 px-2 py-1 text-xs text-amber-700 dark:text-amber-400"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert mt-0.5 h-3 w-3 shrink-0" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path></svg>Recovered — what the later steps used came from the fallback, not from the registry.</p>
            <button type="button" aria-expanded="false" aria-controls="_R_0_-attempts-1" class="mt-2 inline-flex items-center gap-1 rounded-md text-xs text-muted-foreground transition hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down h-3 w-3 transition-transform" aria-hidden="true"><path d="m6 9 6 6 6-6"></path></svg>2 attempts</button>
          </div>
        </div>
      </li>
      <li class="px-5 py-4">
        <div class="flex gap-3">
          <span class="mt-1.5 flex h-4 w-4 shrink-0 items-center justify-center">
            <span aria-hidden="true" class="h-2 w-2 rounded-full border border-transparent bg-destructive"></span>
          </span>
          <div class="min-w-0 flex-1">
            <p class="text-sm font-medium text-foreground">Apply the migration<span class="sr-only"> — Failed</span></p>
            <p class="mt-0.5 text-xs text-muted-foreground">Blocked. The migration wants a lock the reporting job is holding.</p>
            <button type="button" aria-expanded="true" aria-controls="_R_0_-attempts-2" class="mt-2 inline-flex items-center gap-1 rounded-md text-xs text-muted-foreground transition hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-chevron-down h-3 w-3 transition-transform rotate-180" aria-hidden="true"><path d="m6 9 6 6 6-6"></path></svg>3 attempts</button>
            <ul id="_R_0_-attempts-2" class="mt-2 space-y-1 border-s border-border ps-3">
              <li class="flex flex-wrap items-baseline gap-x-2 text-xs text-muted-foreground">
                <span class="font-medium text-foreground">Attempt 1</span>
                <span class="min-w-0 flex-1">Lock not available after 60s</span>
                <span class="tabular-nums">60s · $0.04</span>
              </li>
              <li class="flex flex-wrap items-baseline gap-x-2 text-xs text-muted-foreground">
                <span class="font-medium text-foreground">Attempt 2</span>
                <span class="min-w-0 flex-1">Lock not available after 60s</span>
                <span class="tabular-nums">60s · $0.04</span>
              </li>
              <li class="flex flex-wrap items-baseline gap-x-2 text-xs text-muted-foreground">
                <span class="font-medium text-foreground">Attempt 3</span>
                <span class="min-w-0 flex-1">Lock not available after 60s</span>
                <span class="tabular-nums">60s · $0.04</span>
              </li>
            </ul>
          </div>
        </div>
      </li>
      <li class="px-5 py-4">
        <div class="flex gap-3">
          <span class="mt-1.5 flex h-4 w-4 shrink-0 items-center justify-center">
            <span aria-hidden="true" class="h-2 w-2 rounded-full border border-transparent bg-border"></span>
          </span>
          <div class="min-w-0 flex-1">
            <p class="text-sm font-medium text-muted-foreground">Restart the workers<span class="sr-only"> — Waiting</span></p>
            <p class="mt-0.5 text-xs text-muted-foreground">Not started — blocked by the step above.</p>
          </div>
        </div>
      </li>
      <li class="px-5 py-4">
        <div class="flex gap-3">
          <span class="mt-1.5 flex h-4 w-4 shrink-0 items-center justify-center">
            <span aria-hidden="true" class="h-2 w-2 rounded-full border border-transparent bg-border"></span>
          </span>
          <div class="min-w-0 flex-1">
            <p class="text-sm font-medium text-muted-foreground">Verify the health check<span class="sr-only"> — Waiting</span></p>
            <p class="mt-0.5 text-xs text-muted-foreground">Not started.</p>
          </div>
        </div>
      </li>
    </ol>
    <div role="alert" class="border-t border-border bg-destructive/5 px-5 py-4">
      <h3 class="flex items-center gap-2 text-sm font-semibold text-foreground"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-triangle-alert h-4 w-4 text-destructive" aria-hidden="true"><path d="m21.73 18-8-14a2 2 0 0 0-3.48 0l-8 14A2 2 0 0 0 4 21h16a2 2 0 0 0 1.73-3"></path><path d="M12 9v4"></path><path d="M12 17h.01"></path></svg>Apply the migration could not complete</h3>
      <div class="mt-3">
        <div class="flex items-center justify-between">
          <p class="text-xs font-medium text-foreground">What the tool returned</p>
          <button type="button" class="inline-flex h-7 items-center gap-1.5 rounded-lg px-2 text-xs text-muted-foreground transition hover:bg-muted hover:text-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-copy h-3.5 w-3.5" aria-hidden="true"><rect width="14" height="14" x="8" y="8" rx="2" ry="2"></rect><path d="M4 16c-1.1 0-2-.9-2-2V4c0-1.1.9-2 2-2h10c1.1 0 2 .9 2 2"></path></svg>Copy</button>
        </div>
        <pre class="mt-1 overflow-x-auto rounded-lg border border-border bg-background px-3 py-2 font-mono text-xs leading-relaxed text-foreground"><code>ERROR: could not obtain lock on relation &quot;orders&quot; (SQLSTATE 55P03)
  detail: process 4417 holds AccessExclusiveLock
  hint: retrying will queue behind the same lock</code></pre>
      </div>
      <div class="mt-3">
        <p class="text-xs font-medium text-foreground">What the agent thinks that means</p>
        <p class="mt-1 text-xs leading-relaxed text-muted-foreground">The nightly reporting job took an exclusive lock on orders at 02:00 and has not released it. Retrying as-is will queue behind the same lock and time out again. Either wait for that job, or run the migration with a lock timeout and accept a partial apply.</p>
      </div>
      <div class="mt-4 flex flex-wrap gap-2">
        <button type="button" class="inline-flex h-9 items-center gap-1.5 rounded-lg bg-primary px-3 text-sm font-semibold text-primary-foreground transition hover:opacity-90 focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-corner-down-left h-4 w-4" aria-hidden="true"><path d="M20 4v7a4 4 0 0 1-4 4H4"></path><path d="m9 10-5 5 5 5"></path></svg>Retry with a lock timeout</button>
        <button type="button" class="inline-flex h-9 items-center gap-1.5 rounded-lg border border-border bg-background px-3 text-sm font-medium text-foreground transition hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-refresh-cw h-4 w-4" aria-hidden="true"><path d="M3 12a9 9 0 0 1 9-9 9.75 9.75 0 0 1 6.74 2.74L21 8"></path><path d="M21 3v5h-5"></path><path d="M21 12a9 9 0 0 1-9 9 9.75 9.75 0 0 1-6.74-2.74L3 16"></path><path d="M8 16H3v5"></path></svg>Retry unchanged</button>
        <button type="button" class="inline-flex h-9 items-center gap-1.5 rounded-lg border border-border bg-background px-3 text-sm font-medium text-foreground transition hover:bg-muted focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 focus-visible:ring-offset-background"><svg xmlns="http://www.w3.org/2000/svg" width="24" height="24" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" class="lucide lucide-user-round-cog h-4 w-4" aria-hidden="true"><path d="m14.305 19.53.923-.382"></path><path d="m15.228 16.852-.923-.383"></path><path d="m16.852 15.228-.383-.923"></path><path d="m16.852 20.772-.383.924"></path><path d="m19.148 15.228.383-.923"></path><path d="m19.53 21.696-.382-.924"></path><path d="M2 21a8 8 0 0 1 10.434-7.62"></path><path d="m20.772 16.852.924-.383"></path><path d="m20.772 19.148.924.383"></path><circle cx="10" cy="8" r="5"></circle><circle cx="18" cy="18" r="3"></circle></svg>I’ll take it from here</button>
      </div>
    </div>
  </div>
</section>
  • This is rendered HTML, not a translation of the React source. The Tailwind classes carry the design and work in any framework.
  • It is one frame: the component in its initial state, with no props applied beyond the defaults.
  • This block is interactive in React — toggles, menus or form state. None of that survives here; the markup is the closed/default state and the handlers are gone. Re-wire them in your own framework.
  • Requires Tailwind, and the design tokens the classes reference (bg-card, text-muted-foreground, and so on). The template ZIPs ship a globals.css that defines them.

What each framework gets across the whole catalog — effects convert properly; this rung is markup.

For AI

The component, its props, the design tokens it expects and the command that installs it — as one prompt. Paste it into Claude, Cursor, v0 or ChatGPT and what they build around it will match the rest of the catalog instead of inventing its own system.

See the prompt

Used in these pages

Want the whole screen instead of this one section? Open a page and copy it entire.

6 more blocks in Agent Reasoning

All of them free to read, copy and install — no account, no locked tiles, no watermarked preview. The whole catalog is open, and so are the API and the CLI.

Browse Agent Reasoning

Shipping one commercially

Copying the code is free. Putting it in client work or a paid product is what Pro is for — the licence, not the access.

  • A commercial licence for everything in the catalog
  • Unlimited bundle exports, in Vue, Svelte and Tailwind
  • One payment — no subscription, nothing to renew
Pro — $79 once

More Agent Reasoning blocks

View category
Open the full page for this block

Collapsible Thinking Trace

The "thought for 5s" panel: a native details/summary outside, individually expandable steps inside — buttons rather than nested disclosures, which are announced as a disclosure within a disclosure.

Agent Reasoning256 lines1 dep
Open the full page for this block

Tool Call Chips

Dense tool invocations that open into their arguments and result, with status carried by icon and word rather than colour, and errors marked as alerts only once the user opens them.

Agent Reasoning258 lines1 dep
Open the full page for this block

Live Agent Task List

Run status as rows — done, running, failed, queued — with native progress elements, per-task retry buttons that name their task, and progress announced from one small status region instead of every row.

Agent Reasoning234 lines1 dep
Open the full page for this block

Working Indicator with Elapsed Time

The gap before the first token: a shimmer grid, a phase label that advances as the wait lengthens, and a running counter — which is the only honest answer to "is it stuck". The timer is hidden from the live region on purpose.

Agent Reasoning120 lines1 dep
Open the full page for this block

Agent Retry Log

What the agent tried, what failed, and what it did about it — the run history a user needs before they trust a second attempt.

Agent Reasoning153 linesNo deps
Open the full page for this block

Agent Cost Breakdown

What one run cost, per step, so an expensive answer is attributable rather than a monthly surprise.

Agent Reasoning149 linesNo deps