Skip to content

v3: stop swallowing deployment errors and display them better #1020

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 6 commits into from
Apr 10, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/tender-oranges-rhyme.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"trigger.dev": patch
---

Stop swallowing deployment errors and display them better
8 changes: 7 additions & 1 deletion apps/kubernetes-provider/src/taskMonitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -136,6 +136,13 @@ export class TaskMonitor {

const podStatus = this.#getPodStatusSummary(pod.status);
const containerState = this.#getContainerStateSummary(containerStatus.state);
const exitCode = containerState.exitCode ?? -1;

// We use this special exit code to signal any errors were already handled elsewhere
if (exitCode === 111) {
return;
}

const rawLogs = await this.#getLogTail(podName);

this.#logger.log(`${podName} failed with:`, {
Expand All @@ -144,7 +151,6 @@ export class TaskMonitor {
rawLogs,
});

const exitCode = containerState.exitCode ?? -1;
const rawReason = podStatus.reason ?? containerState.reason ?? "";
const message = podStatus.message ?? containerState.message ?? "";

Expand Down
39 changes: 39 additions & 0 deletions apps/webapp/app/components/runs/v3/DeploymentError.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { CodeBlock } from "~/components/code/CodeBlock";
import { Callout } from "~/components/primitives/Callout";
import { Header2 } from "~/components/primitives/Headers";
import type { ErrorData } from "~/presenters/v3/DeploymentPresenter.server";

type DeploymentErrorProps = {
errorData: ErrorData;
};

export function DeploymentError({ errorData }: DeploymentErrorProps) {
return (
<div className="flex flex-col gap-2 rounded-sm border border-rose-500/50 p-3">
<DeploymentErrorHeader title={errorData.name ?? "Error"} titleClassName="text-rose-500" />
{errorData.message && <Callout variant="error">{errorData.message}</Callout>}
{errorData.stack && (
<CodeBlock
showCopyButton={false}
showLineNumbers={false}
code={errorData.stack}
maxLines={20}
/>
)}
</div>
);
}

function DeploymentErrorHeader({
title,
titleClassName,
}: {
title: string;
titleClassName?: string;
}) {
return (
<div className="flex items-center justify-between">
<Header2 className={titleClassName}>{title}</Header2>
</div>
);
}
8 changes: 7 additions & 1 deletion apps/webapp/app/presenters/v3/DeploymentPresenter.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,12 @@ import { User } from "~/models/user.server";
import { safeJsonParse } from "~/utils/json";
import { getUsername } from "~/utils/username";

export type ErrorData = {
name: string;
message: string;
stack?: string;
};

export class DeploymentPresenter {
#prismaClient: PrismaClient;

Expand Down Expand Up @@ -133,7 +139,7 @@ export class DeploymentPresenter {
};
}

#prepareErrorData(errorData: WorkerDeployment["errorData"]) {
#prepareErrorData(errorData: WorkerDeployment["errorData"]): ErrorData | undefined {
if (!errorData) {
return;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import {
TableHeaderCell,
TableRow,
} from "~/components/primitives/Table";
import { DeploymentError } from "~/components/runs/v3/DeploymentError";
import { DeploymentStatus } from "~/components/runs/v3/DeploymentStatus";
import { TaskFunctionName } from "~/components/runs/v3/TaskPath";
import { useOrganization } from "~/hooks/useOrganizations";
Expand Down Expand Up @@ -158,25 +159,7 @@ export default function Page() {
</Table>
</div>
) : deployment.errorData ? (
<div className="flex flex-col">
{deployment.errorData.stack ? (
<CodeBlock
language="markdown"
rowTitle={deployment.errorData.message}
code={deployment.errorData.stack}
maxLines={20}
/>
) : (
<div className="flex flex-col">
<Paragraph
variant="base/bright"
className="w-full border-b border-grid-dimmed py-2.5"
>
{deployment.errorData.message}
</Paragraph>
</div>
)}
</div>
<DeploymentError errorData={deployment.errorData} />
) : null}
</div>
</div>
Expand Down
38 changes: 23 additions & 15 deletions packages/cli-v3/src/workers/prod/entry-point.ts
Original file line number Diff line number Diff line change
Expand Up @@ -417,7 +417,10 @@ class ProdWorker {
}
} catch (e) {
if (e instanceof TaskMetadataParseError) {
logger.error("tasks metadata parse error", { message: e.zodIssues, tasks: e.tasks });
logger.error("tasks metadata parse error", {
zodIssues: e.zodIssues,
tasks: e.tasks,
});

socket.emit("INDEXING_FAILED", {
version: "v1",
Expand All @@ -429,31 +432,35 @@ class ProdWorker {
},
});
} else if (e instanceof UncaughtExceptionError) {
logger.error("uncaught exception", { message: e.originalError.message });
const error = {
name: e.originalError.name,
message: e.originalError.message,
stack: e.originalError.stack,
};

logger.error("uncaught exception", { originalError: error });

socket.emit("INDEXING_FAILED", {
version: "v1",
deploymentId: this.deploymentId,
error: {
name: e.originalError.name,
message: e.originalError.message,
stack: e.originalError.stack,
},
error,
});
} else if (e instanceof Error) {
logger.error("error", { message: e.message });
const error = {
name: e.name,
message: e.message,
stack: e.stack,
};

logger.error("error", { error });

socket.emit("INDEXING_FAILED", {
version: "v1",
deploymentId: this.deploymentId,
error: {
name: e.name,
message: e.message,
stack: e.stack,
},
error,
});
} else if (typeof e === "string") {
logger.error("string error", { message: e });
logger.error("string error", { error: { message: e } });

socket.emit("INDEXING_FAILED", {
version: "v1",
Expand All @@ -477,7 +484,8 @@ class ProdWorker {
}

await setTimeout(200);
process.exit(1);
// Use exit code 111 so we can ignore those failures in the task monitor
process.exit(111);
}
}

Expand Down