Skip to content

Prettier errors and related improvements #1387

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 17 commits into from
Oct 9, 2024
Merged
Show file tree
Hide file tree
Changes from 16 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
6 changes: 6 additions & 0 deletions .changeset/plenty-spoons-build.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
---
"trigger.dev": patch
"@trigger.dev/core": patch
---

Prettier and more specific errors with links to docs
27 changes: 18 additions & 9 deletions apps/coordinator/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -401,13 +401,16 @@ class TaskCoordinator {
success: true,
};
} catch (error) {
logger.error("Error while waiting for checkpointable state", { error });
logger.error("Error while waiting for checkpointable state", {
error,
runId: socket.data.runId,
});

if (error instanceof CheckpointReadinessTimeoutError) {
await crashRun({
name: error.name,
message: `Failed to become checkpointable in ${CHECKPOINTABLE_TIMEOUT_SECONDS}s for ${reason}`,
});
logger.error(
`Failed to become checkpointable in ${CHECKPOINTABLE_TIMEOUT_SECONDS}s for ${reason}`,
{ runId: socket.data.runId }
);

return {
success: false,
Expand Down Expand Up @@ -490,7 +493,7 @@ class TaskCoordinator {
updateAttemptFriendlyId(executionAck.payload.execution.attempt.id);
updateAttemptNumber(executionAck.payload.execution.attempt.number);
} catch (error) {
logger.error("Error", { error });
logger.error("READY_FOR_EXECUTION error", { error, runId: socket.data.runId });

await crashRun({
name: "ReadyForExecutionError",
Expand Down Expand Up @@ -524,7 +527,10 @@ class TaskCoordinator {
}

if (!lazyAttempt.success) {
logger.error("failed to get lazy attempt payload", { runId: socket.data.runId });
logger.error("failed to get lazy attempt payload", {
runId: socket.data.runId,
reason: lazyAttempt.reason,
});

await crashRun({
name: "ReadyForLazyAttemptError",
Expand All @@ -546,7 +552,7 @@ class TaskCoordinator {
return;
}

logger.error("Error", { error });
logger.error("READY_FOR_LAZY_ATTEMPT error", { error, runId: socket.data.runId });

await crashRun({
name: "ReadyForLazyAttemptError",
Expand Down Expand Up @@ -1004,7 +1010,10 @@ class TaskCoordinator {
});

if (!createAttempt?.success) {
logger.debug("no ack while creating attempt", message);
logger.debug("no ack while creating attempt", {
runId: message.runId,
reason: createAttempt?.reason,
});
callback({ success: false, reason: createAttempt?.reason });
return;
}
Expand Down
10 changes: 9 additions & 1 deletion apps/kubernetes-provider/src/taskMonitor.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ import { SimpleLogger } from "@trigger.dev/core/v3/apps";
import { EXIT_CODE_ALREADY_HANDLED, EXIT_CODE_CHILD_NONZERO } from "@trigger.dev/core/v3/apps";
import { setTimeout } from "timers/promises";
import PQueue from "p-queue";
import type { Prettify } from "@trigger.dev/core/v3";
import { TaskRunErrorCodes, type Prettify, type TaskRunInternalError } from "@trigger.dev/core/v3";

type FailureDetails = Prettify<{
exitCode: number;
reason: string;
logs: string;
overrideCompletion: boolean;
errorCode: TaskRunInternalError["code"];
}>;

type IndexFailureHandler = (deploymentId: string, details: FailureDetails) => Promise<any>;
Expand Down Expand Up @@ -160,18 +161,23 @@ export class TaskMonitor {
let reason = rawReason || "Unknown error";
let logs = rawLogs || "";
let overrideCompletion = false;
let errorCode: TaskRunInternalError["code"] = TaskRunErrorCodes.POD_UNKNOWN_ERROR;

switch (rawReason) {
case "Error":
reason = "Unknown error.";
errorCode = TaskRunErrorCodes.POD_UNKNOWN_ERROR;
break;
case "Evicted":
if (message.startsWith("Pod ephemeral local storage usage")) {
reason = "Storage limit exceeded.";
errorCode = TaskRunErrorCodes.DISK_SPACE_EXCEEDED;
} else if (message) {
reason = `Evicted: ${message}`;
errorCode = TaskRunErrorCodes.POD_EVICTED;
} else {
reason = "Evicted for unknown reason.";
errorCode = TaskRunErrorCodes.POD_EVICTED;
}

if (logs.startsWith("failed to try resolving symlinks")) {
Expand All @@ -183,6 +189,7 @@ export class TaskMonitor {
reason = `${
exitCode === EXIT_CODE_CHILD_NONZERO ? "Child process" : "Parent process"
} ran out of memory! Try choosing a machine preset with more memory for this task.`;
errorCode = TaskRunErrorCodes.TASK_PROCESS_OOM_KILLED;
break;
default:
break;
Expand All @@ -193,6 +200,7 @@ export class TaskMonitor {
reason,
logs,
overrideCompletion,
errorCode,
} satisfies FailureDetails;

const app = pod.metadata?.labels?.app;
Expand Down
26 changes: 19 additions & 7 deletions apps/webapp/app/components/runs/v3/RunInspector.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,11 @@
import { CheckIcon, ClockIcon, CloudArrowDownIcon, QueueListIcon } from "@heroicons/react/20/solid";
import { Link } from "@remix-run/react";
import { formatDuration, formatDurationMilliseconds, TaskRunError } from "@trigger.dev/core/v3";
import {
formatDuration,
formatDurationMilliseconds,
TaskRunError,
taskRunErrorEnhancer,
} from "@trigger.dev/core/v3";
import { useEffect } from "react";
import { useTypedFetcher } from "remix-typedjson";
import { ExitIcon } from "~/assets/icons/ExitIcon";
Expand Down Expand Up @@ -553,32 +558,39 @@ function RunTimeline({ run }: { run: RawRun }) {
}

function RunError({ error }: { error: TaskRunError }) {
switch (error.type) {
const enhancedError = taskRunErrorEnhancer(error);

switch (enhancedError.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}
code={enhancedError.raw}
maxLines={20}
/>
</div>
);
}
case "BUILT_IN_ERROR":
case "INTERNAL_ERROR": {
const name = "name" in error ? error.name : error.code;
const name = "name" in enhancedError ? enhancedError.name : enhancedError.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 && (
{enhancedError.message && <Callout variant="error">{enhancedError.message}</Callout>}
{enhancedError.link && (
<Callout variant="docs" to={enhancedError.link.href}>
{enhancedError.link.name}
</Callout>
)}
{enhancedError.stackTrace && (
<CodeBlock
showCopyButton={false}
showLineNumbers={false}
code={error.stackTrace}
code={enhancedError.stackTrace}
maxLines={20}
/>
)}
Expand Down
16 changes: 12 additions & 4 deletions apps/webapp/app/components/runs/v3/SpanEvents.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import {
exceptionEventEnhancer,
isExceptionSpanEvent,
type ExceptionEventProperties,
type SpanEvent as OtelSpanEvent,
Expand Down Expand Up @@ -64,19 +65,26 @@ export function SpanEventError({
spanEvent: OtelSpanEvent;
exception: ExceptionEventProperties;
}) {
const enhancedException = exceptionEventEnhancer(exception);

return (
<div className="flex flex-col gap-2 rounded-sm border border-rose-500/50 px-3 pb-3 pt-2">
<SpanEventHeader
title={exception.type ?? "Error"}
title={enhancedException.type ?? "Error"}
time={spanEvent.time}
titleClassName="text-rose-500"
/>
{exception.message && <Callout variant="error">{exception.message}</Callout>}
{exception.stacktrace && (
{enhancedException.message && <Callout variant="error">{enhancedException.message}</Callout>}
{enhancedException.link && (
<Callout variant="docs" to={enhancedException.link.href}>
{enhancedException.link.name}
</Callout>
)}
{enhancedException.stacktrace && (
<CodeBlock
showCopyButton={false}
showLineNumbers={false}
code={exception.stacktrace}
code={enhancedException.stacktrace}
maxLines={20}
/>
)}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import {
formatDurationMilliseconds,
nanosecondsToMilliseconds,
TaskRunError,
taskRunErrorEnhancer,
} from "@trigger.dev/core/v3";
import { ReactNode, useEffect } from "react";
import { typedjson, useTypedFetcher } from "remix-typedjson";
Expand Down Expand Up @@ -933,12 +934,14 @@ function RunTimelineLine({ title, state }: RunTimelineLineProps) {
}

function RunError({ error }: { error: TaskRunError }) {
switch (error.type) {
const enhancedError = taskRunErrorEnhancer(error);

switch (enhancedError.type) {
case "STRING_ERROR":
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">Error</Header3>
<Callout variant="error">{error.raw}</Callout>
<Callout variant="error">{enhancedError.raw}</Callout>
</div>
);
case "CUSTOM_ERROR": {
Expand All @@ -947,24 +950,29 @@ function RunError({ error }: { error: TaskRunError }) {
<CodeBlock
showCopyButton={false}
showLineNumbers={false}
code={error.raw}
code={enhancedError.raw}
maxLines={20}
/>
</div>
);
}
case "BUILT_IN_ERROR":
case "INTERNAL_ERROR": {
const name = "name" in error ? error.name : error.code;
const name = "name" in enhancedError ? enhancedError.name : enhancedError.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 && (
{enhancedError.message && <Callout variant="error">{enhancedError.message}</Callout>}
{enhancedError.link && (
<Callout variant="docs" to={enhancedError.link.href}>
{enhancedError.link.name}
</Callout>
)}
{enhancedError.stackTrace && (
<CodeBlock
showCopyButton={false}
showLineNumbers={false}
code={error.stackTrace}
code={enhancedError.stackTrace}
maxLines={20}
/>
)}
Expand Down
8 changes: 3 additions & 5 deletions apps/webapp/app/v3/handleSocketIo.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -188,7 +188,7 @@ function createCoordinatorNamespace(io: Server) {
const environment = await findEnvironmentById(message.envId);

if (!environment) {
logger.error("Environment not found", { id: message.envId });
logger.error("CREATE_TASK_RUN_ATTEMPT: Environment not found", message);
return { success: false, reason: "Environment not found" };
}

Expand All @@ -198,16 +198,14 @@ function createCoordinatorNamespace(io: Server) {
const payload = await sharedQueueTasks.getExecutionPayloadFromAttempt(attempt.id, true);

if (!payload) {
logger.error("Failed to retrieve payload after attempt creation", {
id: message.envId,
});
logger.error("Failed to retrieve payload after attempt creation", message);
return { success: false, reason: "Failed to retrieve payload" };
}

return { success: true, executionPayload: payload };
} catch (error) {
logger.error("Error while creating attempt", {
runId: message.runId,
...message,
error,
});
return { success: false };
Expand Down
4 changes: 2 additions & 2 deletions apps/webapp/app/v3/marqs/sharedQueueConsumer.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1160,7 +1160,7 @@ class SharedQueueTasks {
const environment = await findEnvironmentById(envId);

if (!environment) {
logger.error("Environment not found", { id: envId });
logger.error("getLazyAttemptPayload: Environment not found", { runId, envId });
return;
}

Expand All @@ -1182,7 +1182,7 @@ class SharedQueueTasks {
});

if (!run) {
logger.error("Run not found", { id: runId, envId });
logger.error("getLazyAttemptPayload: Run not found", { runId, envId });
return;
}

Expand Down
10 changes: 7 additions & 3 deletions apps/webapp/app/v3/services/crashTaskRun.server.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ import { BaseService } from "./baseService.server";
import { logger } from "~/services/logger.server";
import { AuthenticatedEnvironment } from "~/services/apiAuth.server";
import { CRASHABLE_ATTEMPT_STATUSES, isCrashableRunStatus } from "../taskStatus";
import { sanitizeError } from "@trigger.dev/core/v3";
import { sanitizeError, TaskRunInternalError } from "@trigger.dev/core/v3";
import { FinalizeTaskRunService } from "./finalizeTaskRun.server";

export type CrashTaskRunServiceOptions = {
Expand All @@ -15,6 +15,7 @@ export type CrashTaskRunServiceOptions = {
crashAttempts?: boolean;
crashedAt?: Date;
overrideCompletion?: boolean;
errorCode?: TaskRunInternalError["code"];
};

export class CrashTaskRunService extends BaseService {
Expand All @@ -26,6 +27,8 @@ export class CrashTaskRunService extends BaseService {
...options,
};

logger.debug("CrashTaskRunService.call", { runId, opts });

const taskRun = await this._prisma.taskRun.findFirst({
where: {
id: runId,
Expand Down Expand Up @@ -71,7 +74,7 @@ export class CrashTaskRunService extends BaseService {
attemptStatus: "FAILED",
error: {
type: "INTERNAL_ERROR",
code: "TASK_RUN_CRASHED",
code: opts.errorCode ?? "TASK_RUN_CRASHED",
message: opts.reason,
stackTrace: opts.logs,
},
Expand Down Expand Up @@ -129,6 +132,7 @@ export class CrashTaskRunService extends BaseService {
error: {
reason: string;
logs?: string;
code?: TaskRunInternalError["code"];
}
) {
return await this.traceWithEnv("failAttempt()", environment, async (span) => {
Expand All @@ -146,7 +150,7 @@ export class CrashTaskRunService extends BaseService {
completedAt: failedAt,
error: sanitizeError({
type: "INTERNAL_ERROR",
code: "TASK_RUN_CRASHED",
code: error.code ?? "TASK_RUN_CRASHED",
message: error.reason,
stackTrace: error.logs,
}),
Expand Down
Loading