diff --git a/apps/webapp/app/services/endpointApi.server.ts b/apps/webapp/app/services/endpointApi.server.ts index d20176003..b11a2ac53 100644 --- a/apps/webapp/app/services/endpointApi.server.ts +++ b/apps/webapp/app/services/endpointApi.server.ts @@ -285,6 +285,7 @@ async function safeFetch(url: string, options: RequestInit) { } catch (error) { logger.debug("Error while trying to connect to endpoint", { url, + error, }); } } diff --git a/apps/webapp/app/services/runs/performRunExecution.server.ts b/apps/webapp/app/services/runs/performRunExecution.server.ts index 5527f3e5f..e9e34ab9e 100644 --- a/apps/webapp/app/services/runs/performRunExecution.server.ts +++ b/apps/webapp/app/services/runs/performRunExecution.server.ts @@ -22,6 +22,7 @@ import { safeJsonZodParse } from "~/utils/json"; import { EndpointApi } from "../endpointApi.server"; import { workerQueue } from "../worker.server"; import { formatError } from "~/utils/formatErrors.server"; +import { logger } from "../logger.server"; type FoundRunExecution = NonNullable< Awaited> @@ -290,22 +291,47 @@ export class PerformRunExecutionService { if (!response) { return await this.#failRunExecutionWithRetry(execution, { - message: "Could not connect to the endpoint", + message: `Connection could not be established to the endpoint (${run.endpoint.url})`, }); } const rawBody = await response.text(); if (!response.ok) { + logger.debug("Endpoint responded with non-200 status code", { + status: response.status, + runId: run.id, + endpoint: run.endpoint.url, + }); + const errorBody = safeJsonZodParse(errorParser, rawBody); if (errorBody && errorBody.success) { - return await this.#failRunExecutionWithRetry(execution, errorBody.data); + // Only retry if the error isn't a 4xx + if (response.status >= 400 && response.status <= 499) { + return await this.#failRunExecution( + this.#prismaClient, + execution, + errorBody.data + ); + } else { + return await this.#failRunExecutionWithRetry( + execution, + errorBody.data + ); + } } - return await this.#failRunExecutionWithRetry(execution, { - message: `Endpoint responded with ${response.status} status code`, - }); + // Only retry if the error isn't a 4xx + if (response.status >= 400 && response.status <= 499) { + return await this.#failRunExecution(this.#prismaClient, execution, { + message: `Endpoint responded with ${response.status} status code`, + }); + } else { + return await this.#failRunExecutionWithRetry(execution, { + message: `Endpoint responded with ${response.status} status code`, + }); + } } const safeBody = safeJsonZodParse(parser, rawBody); diff --git a/examples/nextjs-example/src/jobs/edgeCases.ts b/examples/nextjs-example/src/jobs/edgeCases.ts new file mode 100644 index 000000000..414b196a6 --- /dev/null +++ b/examples/nextjs-example/src/jobs/edgeCases.ts @@ -0,0 +1,27 @@ +import { client } from "@/trigger"; +import { Job, eventTrigger } from "@trigger.dev/sdk"; +import { z } from "zod"; + +new Job(client, { + id: "test-long-running-cpu", + name: "Test long running CPU", + version: "0.0.1", + trigger: eventTrigger({ + name: "test.cpu", + schema: z.object({ + iterations: z.number(), + sleepDuration: z.number(), + }), + }), + run: async (payload, io, ctx) => { + console.log(`Running run ${ctx.run.id} at ${new Date().toISOString()}`); + + for (let i = 0; i < payload.iterations ?? 1; i++) { + await new Promise((resolve) => + setTimeout(resolve, payload.sleepDuration ?? 1000) + ); + } + + console.log(`Finishing run ${ctx.run.id} at ${new Date().toISOString()}`); + }, +}); diff --git a/examples/nextjs-example/src/pages/api/trigger.ts b/examples/nextjs-example/src/pages/api/trigger.ts index fee0d0ccd..4672fc088 100644 --- a/examples/nextjs-example/src/pages/api/trigger.ts +++ b/examples/nextjs-example/src/pages/api/trigger.ts @@ -10,6 +10,7 @@ import "@/jobs/resend"; import "@/jobs/schedules"; import "@/jobs/slack"; import "@/jobs/typeform"; +import "@/jobs/edgeCases"; import { createPagesRoute } from "@trigger.dev/nextjs";