HextaUI

Stack Trace

Show a crash the way a developer reads it. Your code first, framework frames folded away, the failing line in context and every cause in order. Parses Node, Chrome, Firefox, Safari, Bun, Deno and Python stacks, with copy, open in editor and Ask to fix.

When an agent’s command, tool call or test fails, the useful part of the error is buried in fifty lines of framework internals. Stack Trace puts the error type and message first, shows your own frames in full contrast with file, line and column, and folds every run of framework frames into one quiet row like “8 framework frames · next”.

parseStackTrace reads V8 stacks from Node, Chrome, Bun and Deno, Firefox and Safari stacks, Vitest frames with the code under them and Python tracebacks, including chained causes from [cause], Caused by: and Python’s “direct cause” and “during handling” sections. It classifies node_modules, node:internal, Next.js internals, site-packages and the standard library as non-app code, and turns file:// and webpack-internal paths into readable project paths. Lines it doesn’t recognise are kept, never dropped.

The frame where your code failed can show its source with the failing line highlighted, using Code Block. Long messages clamp with Show more, Copy gives back the original text, and frames open in your editor through getFrameHref or onFrameClick. The compact variant is a single “Failed: TypeError: …” line for a failed tool call that opens into the same frames.

  1. Get HextaUI Pro

    One payment unlocks every block, including future ones. See pricing.

  2. Add the Pro registry to components.json

    components.json
    {
      "registries": {
        "@hextaui-pro": {
          "url": "https://hextaui.com/r/pro/{name}.json",
          "headers": {
            "Authorization": "Bearer ${HEXTAUI_PRO_TOKEN}"
          }
        }
      }
    }
  3. Add your token

    Create a token on your account page and put it in .env.local as HEXTAUI_PRO_TOKEN.

  4. Add the block

    pnpm dlx shadcn@latest add @hextaui-pro/stack-trace

From a log

Pass the raw text from a terminal, a log or a tool result. getEditorHref turns frames into vscode:// or cursor:// links, and Ask to fix hands you the parsed error to send back to the agent.

"use client"

import { getEditorHref } from "@/components/blocks/stack-trace/parse-stack-trace"
import { StackTrace } from "@/components/blocks/stack-trace/stack-trace"

export function CrashReport({
  log,
  onAsk,
}: {
  log: string
  onAsk: (prompt: string) => void
}) {
  return (
    <StackTrace
      trace={log}
      getFrameHref={(frame) =>
        frame.kind === "app"
          ? getEditorHref(frame, { editor: "cursor", root: "/Users/me/acme" })
          : undefined
      }
      onAskToFix={(error) =>
        onAsk(`Fix this ${error.type}: ${error.message}\n\n${error.raw}`)
      }
    />
  )
}

With the AI SDK

A tool that throws ends in the output-error state with errorText. Render it as a compact StackTrace inside the failed ToolCall, so the step reads as one line until someone opens it.

"use client"

import { useChat } from "@ai-sdk/react"
import {
  getToolName,
  isToolUIPart,
  type DynamicToolUIPart,
  type ToolUIPart,
  type UIMessage,
} from "ai"

import type { ToolCallProps } from "../tool-calls/tool-call"
import { ToolCalls } from "../tool-calls/tool-group"
import { getToolPartStatus } from "../tool-calls/tool-part"
import { getEditorHref } from "@/components/blocks/stack-trace/parse-stack-trace"
import { StackTrace } from "@/components/blocks/stack-trace/stack-trace"

type ToolPart = ToolUIPart | DynamicToolUIPart

function toCall(
  part: ToolPart,
  streaming: boolean,
  onAsk: (text: string) => void
): ToolCallProps {
  const input = (part.input ?? {}) as { command?: string }
  const failed = part.state === "output-error"
  return {
    id: part.toolCallId,
    kind: getToolName(part) === "runCommand" ? "run" : "other",
    name: getToolName(part),
    subject: input.command,
    status: getToolPartStatus(part, { stopped: !streaming }),
    defaultOpen: failed ? true : undefined,
    content: failed ? (
      <StackTrace
        variant="compact"
        trace={part.errorText}
        getFrameHref={(frame) =>
          getEditorHref(frame, { root: process.env.NEXT_PUBLIC_PROJECT_ROOT })
        }
        onAskToFix={(error) =>
          onAsk(
            `The ${getToolName(part)} call failed with ${error.type}: ${error.message}. Fix it.`
          )
        }
      />
    ) : undefined,
  }
}

function AssistantMessage({
  message,
  streaming,
  onAsk,
}: {
  message: UIMessage
  streaming: boolean
  onAsk: (text: string) => void
}) {
  const calls = message.parts
    .filter(isToolUIPart)
    .map((part) => toCall(part, streaming, onAsk))
  return calls.length ? <ToolCalls calls={calls} /> : null
}

export function Chat() {
  const { messages, status, sendMessage } = useChat()
  const busy = status === "submitted" || status === "streaming"

  return messages.map((message, index) =>
    message.role === "assistant" ? (
      <AssistantMessage
        key={message.id}
        message={message}
        streaming={busy && index === messages.length - 1}
        onAsk={(text) => sendMessage({ text })}
      />
    ) : null
  )
}

In a Next.js error boundary

parseError reads an Error object, including its name, message and cause chain. Show the trace in development and a plain message in production.

"use client"

import { Button } from "@/components/ui/button"

import { parseError } from "@/components/blocks/stack-trace/parse-stack-trace"
import { StackTrace } from "@/components/blocks/stack-trace/stack-trace"

export default function Error({
  error,
  reset,
}: {
  error: Error & { digest?: string }
  reset: () => void
}) {
  return (
    <main className="mx-auto flex w-full max-w-2xl flex-col gap-4 p-6">
      {process.env.NODE_ENV === "development" ? (
        <StackTrace label="Crashed" trace={parseError(error)} />
      ) : (
        <p className="text-sm text-muted-foreground">Something went wrong.</p>
      )}
      <Button variant="outline" className="self-start" onClick={reset}>
        Try again
      </Button>
    </main>
  )
}

Just the frames

parseStackTrace and StackFrames work on their own, for example to show a short stack under each failed test.

"use client"

import { parseStackTrace } from "@/components/blocks/stack-trace/parse-stack-trace"
import { StackFrames } from "@/components/blocks/stack-trace/stack-frames"

export function FailedTest({ name, stack }: { name: string; stack: string }) {
  const error = parseStackTrace(stack, { root: "/Users/me/acme" })

  return (
    <div className="flex flex-col gap-1">
      <p className="text-sm">
        {name}: <span className="text-muted-foreground">{error.message}</span>
      </p>
      <StackFrames frames={error.frames} showSource={false} />
    </div>
  )
}

Anatomy

The parts you compose, from the outside in.

PartDescription
StackTraceThe error: type, message, actions, frames and causes. Default is a card; compact is one line that expands.
StackFramesThe frame list on its own, with folded framework frames and the source excerpt.
parseStackTrace, parseErrorPure parsers that turn text or an Error object into a ParsedStackTrace.
getEditorHrefBuilds a vscode://, cursor://, windsurf:// or zed:// link for a frame.

StackTrace

Also accepts every div prop except children.

PropTypeDefault
traceThe raw stack text, or a trace you parsed and enriched yourself.
string | ParsedStackTrace–
variantcompact renders one “Failed: …” line that opens into the frames, for use inside a ToolCall.
"default" | "compact""default"
labelThe status word before the error type, such as “Crashed” or “Test failed”.
string"Failed"
rootProject root, stripped from absolute paths. Found from node_modules or .venv when left out.
string–
getFrameHrefTurns a frame into a link, for example with getEditorHref.
(frame: StackFrame) => string | undefined–
onFrameClickCalled when a frame is clicked. Call event.preventDefault() to handle links yourself.
(frame: StackFrame, event: MouseEvent) => void–
onAskToFixShows Ask to fix and receives the parsed error.
(trace: ParsedStackTrace) => void–
askLabelLabel for the Ask to fix button.
string"Ask to fix"
showSourceShow source lines under frames, and the excerpt for your top frame.
booleantrue
foldFold runs of two or more framework or internal frames.
booleantrue
messageLinesLines of message shown before Show more.
number3
openControlled open state of the compact variant.
boolean–
defaultOpenWhether the compact variant starts open.
booleanfalse
onOpenChangeCalled when the compact variant opens or closes.
(open: boolean) => void–

StackFrames

Also accepts every ol prop except children.

PropTypeDefault
framesFrames from parseStackTrace, innermost first.
StackFrame[]–
foldFold runs of non-app frames.
booleantrue
showSourceShow source lines and the top frame’s excerpt.
booleantrue
getFrameHrefTurns a frame into a link.
(frame: StackFrame) => string | undefined–
onFrameClickCalled when a frame is clicked.
(frame: StackFrame, event: MouseEvent) => void–

parseStackTrace(text, options)

Returns a ParsedStackTrace. parseError(error, options) does the same for an Error object and its cause.

PropTypeDefault
textA stack, a traceback or raw log output around one.
string–
options.rootProject root to strip from absolute paths.
string–
options.classifyOverride the kind of a frame, for example to treat a vendored folder as framework code.
(frame: StackFrame) => StackFrameKind | undefined–
PropTypeDefault
typeError type, such as TypeError or billing.plans.PlanNotFound.
string–
messageThe message, which can span several lines.
string–
framesInnermost first for every language, so frames[0] is where it threw.
StackFrame[]–
rawThe original text, used by Copy.
string–
languageWhich format was parsed.
"javascript" | "python"–
codeError code, such as ERR_MODULE_NOT_FOUND or ECONNREFUSED.
string–
detailsExtra properties Node prints after the frames, such as errno and syscall.
string–
preambleLog lines that came before the error.
string–
causeThe next error in the chain, with relation set to cause or context.
ParsedStackTrace–
PropTypeDefault
kindYour code, a dependency, or the runtime itself.
"app" | "framework" | "internal"–
fnFunction name. Undefined for anonymous functions, which show only their location.
string–
fileReadable path relative to the project, such as app/api/users/route.ts.
string–
pathThe location as reported, with file:// and webpack prefixes removed. Absolute when the stack had one.
string–
line, columnPosition in the file.
number–
packagePackage a non-app frame belongs to, such as next or fastapi.
string–
source, sourceStartSource lines and the number of the first one. Several lines show as an excerpt.
string, number–
rawThe original line. Lines the parser doesn’t recognise only have raw.
string–

getEditorHref(frame, options)

Returns undefined for frames without a file on disk.

PropTypeDefault
options.editorThe URL scheme to use.
"vscode" | "cursor" | "windsurf" | "zed""vscode"
options.rootJoined with relative paths, such as those from webpack-internal frames.
string–
KeyAction
TabMoves through the actions, Show more, frame links and folded frame rows.
EnterSpaceOpens or closes folded frames, or the compact line.
EnterOpens a frame link in your editor.
  • Frames are an ordered list, so screen readers announce how many there are and the position of each.
  • Folded rows are buttons with aria-expanded, and folded frames stay findable with the browser’s find in page, which opens the fold.
  • The default view is a group named by its status and error type, and each cause is a section named “Caused by …”.
  • Status is never color alone: the failure is written out as text next to its icon, and Copy announces “Copied”.

Built with

The free HextaUI components Stack Trace is made from. Each one installs on its own.

Code

4 files, added to components/blocks/stack-trace.