import type { TaskRunExecutionResult, TaskRunFailedExecutionResult, TaskRunSuccessfulExecutionResult, } from "@trigger.dev/core/v3"; import { TaskRunError, TaskRunErrorCodes } from "@trigger.dev/core/v3"; import type { BatchTaskRunItemStatus as BatchTaskRunItemStatusType, TaskRun, TaskRunAttempt, TaskRunStatus as TaskRunStatusType, } from "@trigger.dev/database"; import { assertNever } from "assert-never"; import { BatchTaskRunItemStatus, TaskRunAttemptStatus, TaskRunStatus } from "~/database-types"; import { logger } from "~/services/logger.server"; const SUCCESSFUL_STATUSES = [TaskRunStatus.COMPLETED_SUCCESSFULLY]; const FAILURE_STATUSES = [ TaskRunStatus.CANCELED, TaskRunStatus.INTERRUPTED, TaskRunStatus.COMPLETED_WITH_ERRORS, TaskRunStatus.SYSTEM_FAILURE, TaskRunStatus.CRASHED, ]; export type TaskRunWithAttempts = TaskRun & { attempts: TaskRunAttempt[]; }; export function executionResultForTaskRun( taskRun: TaskRunWithAttempts ): TaskRunExecutionResult | undefined { if (SUCCESSFUL_STATUSES.includes(taskRun.status)) { // find the last attempt that was successful const attempt = taskRun.attempts.find((a) => a.status === TaskRunAttemptStatus.COMPLETED); if (!attempt) { logger.error("Task run is successful but no successful attempt found", { taskRunId: taskRun.id, taskRunStatus: taskRun.status, taskRunAttempts: taskRun.attempts.map((a) => a.status), }); return undefined; } return { ok: true, id: taskRun.friendlyId, taskIdentifier: taskRun.taskIdentifier, output: attempt.output ?? undefined, outputType: attempt.outputType, } satisfies TaskRunSuccessfulExecutionResult; } if (FAILURE_STATUSES.includes(taskRun.status)) { if (taskRun.status === TaskRunStatus.CANCELED) { return { ok: false, id: taskRun.friendlyId, taskIdentifier: taskRun.taskIdentifier, error: { type: "INTERNAL_ERROR", code: TaskRunErrorCodes.TASK_RUN_CANCELLED, }, } satisfies TaskRunFailedExecutionResult; } const attempt = taskRun.attempts.find((a) => a.status === TaskRunAttemptStatus.FAILED); if (!attempt) { logger.error("Task run is failed but no failed attempt found", { taskRunId: taskRun.id, taskRunStatus: taskRun.status, taskRunAttempts: taskRun.attempts.map((a) => a.status), }); return undefined; } const error = TaskRunError.safeParse(attempt.error); if (!error.success) { logger.error("Failed to parse error from failed task run attempt", { taskRunId: taskRun.id, taskRunStatus: taskRun.status, taskRunAttempts: taskRun.attempts.map((a) => a.status), error: attempt.error, }); return { ok: false, id: taskRun.friendlyId, taskIdentifier: taskRun.taskIdentifier, error: { type: "INTERNAL_ERROR", code: TaskRunErrorCodes.CONFIGURED_INCORRECTLY, }, } satisfies TaskRunFailedExecutionResult; } return { ok: false, id: taskRun.friendlyId, taskIdentifier: taskRun.taskIdentifier, error: error.data, } satisfies TaskRunFailedExecutionResult; } } export function batchTaskRunItemStatusForRunStatus( status: TaskRunStatusType ): BatchTaskRunItemStatusType { switch (status) { case TaskRunStatus.COMPLETED_SUCCESSFULLY: return BatchTaskRunItemStatus.COMPLETED; case TaskRunStatus.CANCELED: case TaskRunStatus.INTERRUPTED: case TaskRunStatus.COMPLETED_WITH_ERRORS: case TaskRunStatus.SYSTEM_FAILURE: case TaskRunStatus.CRASHED: case TaskRunStatus.EXPIRED: case TaskRunStatus.TIMED_OUT: return BatchTaskRunItemStatus.FAILED; case TaskRunStatus.PENDING: case TaskRunStatus.PENDING_VERSION: case TaskRunStatus.WAITING_FOR_DEPLOY: case TaskRunStatus.WAITING_TO_RESUME: case TaskRunStatus.RETRYING_AFTER_FAILURE: case TaskRunStatus.DEQUEUED: case TaskRunStatus.EXECUTING: case TaskRunStatus.PAUSED: case TaskRunStatus.DELAYED: return BatchTaskRunItemStatus.PENDING; default: assertNever(status); } }