Stack Trace

Affichez un plantage comme un développeur le lit. Votre code d’abord, les frames du framework repliées, la ligne fautive en contexte et chaque cause dans l’ordre. Analyse les stacks de Node, Chrome, Firefox, Safari, Bun, Deno et Python, avec copie, ouverture dans l’éditeur et demande de correction.

Quand la commande, l’appel d’outil ou le test d’un agent échoue, la partie utile de l’erreur est enfouie sous cinquante lignes d’internes du framework. Stack Trace place le type d’erreur et le message en premier, affiche vos propres frames en plein contraste avec fichier, ligne et colonne, et replie chaque suite de frames du framework en une ligne discrète comme « 8 framework frames · next ».

parseStackTrace lit les stacks V8 de Node, Chrome, Bun et Deno, les stacks Firefox et Safari, les frames Vitest avec le code affiché dessous et les tracebacks Python, y compris les causes chaînées issues de [cause], Caused by: et des sections Python « direct cause » et « during handling ». Il classe node_modules, node:internal, les internes de Next.js, site-packages et la bibliothèque standard comme code hors de l’app, et transforme les chemins file:// et webpack-internal en chemins de projet lisibles. Les lignes qu’il ne reconnaît pas sont conservées, jamais supprimées.

La frame où votre code a échoué peut afficher sa source avec la ligne fautive mise en évidence, grâce à Code Block. Les longs messages sont tronqués avec Show more, Copy rend le texte d’origine, et les frames s’ouvrent dans votre éditeur via getFrameHref ou onFrameClick. La variante compacte est une seule ligne « Failed: TypeError: … » pour un appel d’outil en échec, qui s’ouvre sur les mêmes frames.

  1. Procurez-vous HextaUI Pro

    Un seul paiement débloque tous les blocs, y compris les prochains. Voir les tarifs.

  2. Ajouter le registre Pro à components.json

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

    Créez un token sur votre page de compte et placez-le dans .env.local sous le nom HEXTAUI_PRO_TOKEN.

  4. Ajouter le block

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

Depuis un log

Passez le texte brut d’un terminal, d’un log ou d’un résultat d’outil. getEditorHref transforme les frames en liens vscode:// ou cursor://, et Ask to fix vous donne l’erreur analysée à renvoyer à l’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}`)
      }
    />
  )
}

Avec AI SDK

Un outil qui lève une erreur se termine dans l’état output-error avec errorText. Affichez-le comme un StackTrace compact dans le ToolCall en échec, pour que l’étape tienne sur une ligne jusqu’à ce que quelqu’un l’ouvre.

"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
  )
}

Dans une error boundary Next.js

parseError lit un objet Error, avec son nom, son message et sa chaîne de causes. Affichez la trace en développement et un message simple en 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>
  )
}

Juste les frames

parseStackTrace et StackFrames fonctionnent seuls, par exemple pour afficher une courte stack sous chaque test en échec.

"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>
  )
}

Anatomie

Les parties à composer, de l’extérieur vers l’intérieur.

PartieDescription
StackTraceL’erreur : type, message, actions, frames et causes. Par défaut, c’est une card ; compact est une ligne qui se déplie.
StackFramesLa liste de frames seule, avec les frames du framework repliées et l’extrait de source.
parseStackTrace, parseErrorParseurs purs qui transforment du texte ou un objet Error en ParsedStackTrace.
getEditorHrefCrée un lien vscode://, cursor://, windsurf:// ou zed:// pour une frame.

StackTrace

Accepte aussi toutes les props de div sauf children.

PropTypePar défaut
traceLe texte brut de la stack, ou une trace que vous avez analysée et enrichie vous-même.
string | ParsedStackTrace–
variantcompact affiche une ligne « Failed: … » qui s’ouvre sur les frames, à utiliser dans un ToolCall.
"default" | "compact""default"
labelLe mot d’état avant le type d’erreur, comme « Crashed » ou « Test failed ».
string"Failed"
rootRacine du projet, retirée des chemins absolus. Déduite de node_modules ou .venv si elle est omise.
string–
getFrameHrefTransforme une frame en lien, par exemple avec getEditorHref.
(frame: StackFrame) => string | undefined–
onFrameClickAppelé quand on clique sur une frame. Appelez event.preventDefault() pour gérer les liens vous-même.
(frame: StackFrame, event: MouseEvent) => void–
onAskToFixAffiche Ask to fix et reçoit l’erreur analysée.
(trace: ParsedStackTrace) => void–
askLabelLibellé du bouton Ask to fix.
string"Ask to fix"
showSourceAffiche les lignes de source sous les frames, et l’extrait de votre frame du haut.
booleantrue
foldReplie les suites de deux frames ou plus du framework ou internes.
booleantrue
messageLinesLignes du message affichées avant Show more.
number3
openÉtat d’ouverture contrôlé de la variante compacte.
boolean–
defaultOpenIndique si la variante compacte démarre ouverte.
booleanfalse
onOpenChangeAppelé quand la variante compacte s’ouvre ou se ferme.
(open: boolean) => void–

StackFrames

Accepte aussi toutes les props de ol sauf children.

PropTypePar défaut
framesFrames issues de parseStackTrace, la plus interne en premier.
StackFrame[]–
foldReplie les suites de frames qui ne viennent pas de l’app.
booleantrue
showSourceAffiche les lignes de source et l’extrait de la frame du haut.
booleantrue
getFrameHrefTransforme une frame en lien.
(frame: StackFrame) => string | undefined–
onFrameClickAppelé quand on clique sur une frame.
(frame: StackFrame, event: MouseEvent) => void–

parseStackTrace(text, options)

Renvoie un ParsedStackTrace. parseError(error, options) fait de même pour un objet Error et sa cause.

PropTypePar défaut
textUne stack, un traceback ou la sortie de log brute qui l’entoure.
string–
options.rootRacine du projet à retirer des chemins absolus.
string–
options.classifyRemplace le type d’une frame, par exemple pour traiter un dossier vendored comme du code de framework.
(frame: StackFrame) => StackFrameKind | undefined–
PropTypePar défaut
typeType d’erreur, comme TypeError ou billing.plans.PlanNotFound.
string–
messageLe message, qui peut tenir sur plusieurs lignes.
string–
framesLa plus interne en premier dans tous les langages, donc frames[0] est l’endroit où l’erreur a été levée.
StackFrame[]–
rawLe texte d’origine, utilisé par Copy.
string–
languageLe format qui a été analysé.
"javascript" | "python"–
codeCode d’erreur, comme ERR_MODULE_NOT_FOUND ou ECONNREFUSED.
string–
detailsPropriétés supplémentaires que Node affiche après les frames, comme errno et syscall.
string–
preambleLignes de log apparues avant l’erreur.
string–
causeL’erreur suivante de la chaîne, avec relation à cause ou context.
ParsedStackTrace–
PropTypePar défaut
kindVotre code, une dépendance ou le runtime lui-même.
"app" | "framework" | "internal"–
fnNom de la fonction. Undefined pour les fonctions anonymes, qui n’affichent que leur emplacement.
string–
fileChemin lisible relatif au projet, comme app/api/users/route.ts.
string–
pathL’emplacement tel que rapporté, sans les préfixes file:// et webpack. Absolu quand la stack en avait un.
string–
line, columnPosition dans le fichier.
number–
packagePaquet auquel appartient une frame hors de l’app, comme next ou fastapi.
string–
source, sourceStartLignes de source et numéro de la première. Plusieurs lignes s’affichent comme extrait.
string, number–
rawLa ligne d’origine. Les lignes que le parseur ne reconnaît pas n’ont que raw.
string–

getEditorHref(frame, options)

Renvoie undefined pour les frames sans fichier sur le disque.

PropTypePar défaut
options.editorLe schéma d’URL à utiliser.
"vscode" | "cursor" | "windsurf" | "zed""vscode"
options.rootJoint aux chemins relatifs, comme ceux des frames webpack-internal.
string–
ToucheAction
TabParcourt les actions, Show more, les liens de frames et les lignes de frames repliées.
EnterSpaceOuvre ou ferme les frames repliées, ou la ligne compacte.
EnterOuvre le lien d’une frame dans votre éditeur.
  • Les frames forment une liste ordonnée : les lecteurs d’écran annoncent leur nombre et la position de chacune.
  • Les lignes repliées sont des boutons avec aria-expanded, et les frames repliées restent trouvables avec la recherche dans la page du navigateur, qui ouvre le pli.
  • La vue par défaut est un groupe nommé d’après son état et son type d’erreur, et chaque cause est une section nommée « Caused by … ».
  • L’état ne repose jamais sur la couleur seule : l’échec est écrit en texte à côté de son icône, et Copy annonce « Copied ».

Construit avec

Les composants HextaUI gratuits dont Stack Trace est constitué. Chacun s’installe séparément.

Code

4 fichiers, ajoutés à components/blocks/stack-trace.