Skip to content

Commit 40c4064

Browse files
authored
fix(webapp): show errors on AI tool call and embed spans in the run inspector (#4653)
## Summary When an AI SDK tool call failed inside a run, the span showed up under the "Errors only" filter but the span inspector gave no hint of what went wrong. The exception was recorded on the span all along; the `ai.toolCall` and `ai.embed` inspector views just never rendered span events. Failed tool call and embedding spans now show the standard error block (message plus stack trace) below the Input section. ## Root cause Generic spans render exception span events via the `SpanEvents` component, but the AI-specific span entities replace the whole panel with their own layout and dropped the events entirely. The span's events are now passed into `AIToolCallSpanDetails` and `AIEmbedSpanDetails` and rendered with the same `SpanEvents` component the generic view uses. Errored generation spans (`ai.generateText` and friends) use a tabbed view and still don't surface errors; that needs its own design pass and is left for a follow-up.
1 parent a55f7cd commit 40c4064

4 files changed

Lines changed: 38 additions & 4 deletions

File tree

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
---
2+
area: webapp
3+
type: fix
4+
---
5+
6+
Failed AI SDK tool call and embedding spans now show the error message and stack trace in the run inspector, below the tool input.

apps/webapp/app/components/runs/v3/ai/AIEmbedSpanDetails.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,7 @@
1+
import type { SpanEvent as OtelSpanEvent } from "@trigger.dev/core/v3";
12
import { Header3 } from "~/components/primitives/Headers";
23
import { Paragraph } from "~/components/primitives/Paragraph";
4+
import { SpanEvents } from "~/components/runs/v3/SpanEvents";
35
import { formatDuration } from "./aiHelpers";
46
import { SpanMetricRow as MetricRow } from "./SpanMetricRow";
57

@@ -35,7 +37,13 @@ export function extractAIEmbedData(
3537
};
3638
}
3739

38-
export function AIEmbedSpanDetails({ data }: { data: AIEmbedData }) {
40+
export function AIEmbedSpanDetails({
41+
data,
42+
spanEvents,
43+
}: {
44+
data: AIEmbedData;
45+
spanEvents?: OtelSpanEvent[];
46+
}) {
3947
return (
4048
<div className="flex h-full flex-col overflow-hidden">
4149
<div className="flex-1 overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control">
@@ -58,6 +66,12 @@ export function AIEmbedSpanDetails({ data }: { data: AIEmbedData }) {
5866
</div>
5967
</div>
6068
)}
69+
70+
{spanEvents && spanEvents.some((event) => !event.name.startsWith("trigger.dev/")) && (
71+
<div className="py-2.5">
72+
<SpanEvents spanEvents={spanEvents} />
73+
</div>
74+
)}
6175
</div>
6276
</div>
6377
</div>

apps/webapp/app/components/runs/v3/ai/AIToolCallSpanDetails.tsx

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
1+
import type { SpanEvent as OtelSpanEvent } from "@trigger.dev/core/v3";
12
import { Header3 } from "~/components/primitives/Headers";
23
import { CodeBlock } from "~/components/code/CodeBlock";
34
import { TruncatedCopyableValue } from "~/components/primitives/TruncatedCopyableValue";
5+
import { SpanEvents } from "~/components/runs/v3/SpanEvents";
46
import { formatDuration, tryPrettyJson } from "./aiHelpers";
57
import { SpanMetricRow as MetricRow } from "./SpanMetricRow";
68

@@ -36,7 +38,13 @@ export function extractAIToolCallData(
3638
};
3739
}
3840

39-
export function AIToolCallSpanDetails({ data }: { data: AIToolCallData }) {
41+
export function AIToolCallSpanDetails({
42+
data,
43+
spanEvents,
44+
}: {
45+
data: AIToolCallData;
46+
spanEvents?: OtelSpanEvent[];
47+
}) {
4048
return (
4149
<div className="flex h-full flex-col overflow-hidden">
4250
<div className="flex-1 overflow-y-auto scrollbar-thin scrollbar-track-transparent scrollbar-thumb-surface-control">
@@ -68,6 +76,12 @@ export function AIToolCallSpanDetails({ data }: { data: AIToolCallData }) {
6876
/>
6977
</div>
7078
)}
79+
80+
{spanEvents && spanEvents.some((event) => !event.name.startsWith("trigger.dev/")) && (
81+
<div className="py-2.5">
82+
<SpanEvents spanEvents={spanEvents} />
83+
</div>
84+
)}
7185
</div>
7286
</div>
7387
</div>

apps/webapp/app/routes/resources.orgs.$organizationSlug.projects.$projectParam.env.$envParam.runs.$runParam.spans.$spanParam/route.tsx

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1777,7 +1777,7 @@ function SpanEntity({ span }: { span: Span }) {
17771777
<div className="px-3">
17781778
<SpanHorizontalTimeline startTime={span.startTime} duration={span.duration} />
17791779
</div>
1780-
<AIToolCallSpanDetails data={span.entity.object} />
1780+
<AIToolCallSpanDetails data={span.entity.object} spanEvents={span.events} />
17811781
</div>
17821782
);
17831783
}
@@ -1787,7 +1787,7 @@ function SpanEntity({ span }: { span: Span }) {
17871787
<div className="px-3">
17881788
<SpanHorizontalTimeline startTime={span.startTime} duration={span.duration} />
17891789
</div>
1790-
<AIEmbedSpanDetails data={span.entity.object} />
1790+
<AIEmbedSpanDetails data={span.entity.object} spanEvents={span.events} />
17911791
</div>
17921792
);
17931793
}

0 commit comments

Comments
 (0)