Added error from the run attempt with nice display (#1249)

This commit is contained in:
Matt Aitken
2024-08-07 15:50:45 +01:00
committed by GitHub
parent 6270abaab8
commit e2d3358271
2 changed files with 75 additions and 20 deletions
@@ -1,9 +1,9 @@
import { Context, MachinePresetName, prettyPrintPacket } from "@trigger.dev/core/v3";
import { MachinePresetName, prettyPrintPacket, TaskRunError } from "@trigger.dev/core/v3";
import { FINISHED_STATUSES, RUNNING_STATUSES } from "~/components/runs/v3/TaskRunStatus";
import { eventRepository } from "~/v3/eventRepository.server";
import { BasePresenter } from "./basePresenter.server";
import { machineDefinition } from "@trigger.dev/platform/v3";
import { machinePresetFromName } from "~/v3/machinePresets.server";
import { FINAL_ATTEMPT_STATUSES } from "~/v3/taskStatus";
import { BasePresenter } from "./basePresenter.server";
type Result = Awaited<ReturnType<SpanPresenter["call"]>>;
export type Span = NonNullable<NonNullable<Result>["span"]>;
@@ -57,6 +57,7 @@ export class SpanPresenter extends BasePresenter {
async getRun(spanId: string) {
const run = await this._replica.taskRun.findFirst({
select: {
id: true,
traceId: true,
//metadata
number: true,
@@ -113,17 +114,6 @@ export class SpanPresenter extends BasePresenter {
payload: true,
payloadType: true,
maxAttempts: true,
//finished attempt
attempts: {
select: {
output: true,
outputType: true,
error: true,
},
where: {
status: "COMPLETED",
},
},
project: {
include: {
organization: true,
@@ -145,9 +135,23 @@ export class SpanPresenter extends BasePresenter {
return;
}
const finishedAttempt = run.attempts.at(0);
const finishedAttempt = await this._replica.taskRunAttempt.findFirst({
select: {
output: true,
outputType: true,
error: true,
},
where: {
status: { in: FINAL_ATTEMPT_STATUSES },
taskRunId: run.id,
},
orderBy: {
createdAt: "desc",
},
});
const output =
finishedAttempt === undefined
finishedAttempt === null
? undefined
: finishedAttempt.outputType === "application/store"
? `/resources/packets/${run.runtimeEnvironment.id}/${finishedAttempt.output}`
@@ -162,6 +166,19 @@ export class SpanPresenter extends BasePresenter {
? await prettyPrintPacket(run.payload, run.payloadType ?? undefined)
: undefined;
let error: TaskRunError | undefined = undefined;
if (finishedAttempt?.error) {
const result = TaskRunError.safeParse(finishedAttempt.error);
if (result.success) {
error = result.data;
} else {
error = {
type: "CUSTOM_ERROR",
raw: JSON.stringify(finishedAttempt.error),
};
}
}
const span = await eventRepository.getSpan(spanId, run.traceId);
const context = {
@@ -247,8 +264,8 @@ export class SpanPresenter extends BasePresenter {
payloadType: run.payloadType,
output,
outputType: finishedAttempt?.outputType ?? "application/json",
error,
links: span?.links,
events: span?.events,
context: JSON.stringify(context, null, 2),
};
}
@@ -5,6 +5,7 @@ import {
formatDuration,
formatDurationMilliseconds,
nanosecondsToMilliseconds,
TaskRunError,
} from "@trigger.dev/core/v3";
import { ReactNode, useEffect } from "react";
import { typedjson, useTypedFetcher } from "remix-typedjson";
@@ -12,8 +13,9 @@ import { ExitIcon } from "~/assets/icons/ExitIcon";
import { CodeBlock } from "~/components/code/CodeBlock";
import { EnvironmentLabel } from "~/components/environments/EnvironmentLabel";
import { Button, LinkButton } from "~/components/primitives/Buttons";
import { Callout } from "~/components/primitives/Callout";
import { DateTime, DateTimeAccurate } from "~/components/primitives/DateTime";
import { Header2 } from "~/components/primitives/Headers";
import { Header2, Header3 } from "~/components/primitives/Headers";
import { Paragraph } from "~/components/primitives/Paragraph";
import * as Property from "~/components/primitives/PropertyTable";
import { Spinner } from "~/components/primitives/Spinner";
@@ -606,8 +608,8 @@ function RunBody({
{run.payload !== undefined && (
<PacketDisplay data={run.payload} dataType={run.payloadType} title="Payload" />
)}
{run.events !== undefined && run.events.length > 0 ? (
<SpanEvents spanEvents={run.events} />
{run.error !== undefined ? (
<RunError error={run.error} />
) : run.output !== undefined ? (
<PacketDisplay data={run.output} dataType={run.outputType} title="Output" />
) : null}
@@ -791,6 +793,42 @@ function RunTimelineLine({ title, state }: RunTimelineLineProps) {
);
}
function RunError({ error }: { error: TaskRunError }) {
switch (error.type) {
case "STRING_ERROR":
case "CUSTOM_ERROR": {
return (
<div className="flex flex-col gap-2 rounded-sm border border-rose-500/50 px-3 pb-3 pt-2">
<CodeBlock
showCopyButton={false}
showLineNumbers={false}
code={error.raw}
maxLines={20}
/>
</div>
);
}
case "BUILT_IN_ERROR":
case "INTERNAL_ERROR": {
const name = "name" in error ? error.name : error.code;
return (
<div className="flex flex-col gap-2 rounded-sm border border-rose-500/50 px-3 pb-3 pt-2">
<Header3 className="text-rose-500">{name}</Header3>
{error.message && <Callout variant="error">{error.message}</Callout>}
{error.stackTrace && (
<CodeBlock
showCopyButton={false}
showLineNumbers={false}
code={error.stackTrace}
maxLines={20}
/>
)}
</div>
);
}
}
}
function PacketDisplay({
data,
dataType,