Skip to content

Commit ab651f8

Browse files
fix(core): retry TASK_MIDDLEWARE_ERROR under the task's retry policy
1 parent d35bf04 commit ab651f8

3 files changed

Lines changed: 19 additions & 5 deletions

File tree

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
---
2+
"@trigger.dev/core": patch
3+
---
4+
5+
Retry TASK_MIDDLEWARE_ERROR under the task's retry policy instead of failing the run on the first attempt. The error was already classified as retryable by shouldRetryError, but shouldLookupRetrySettings did not include it, so the retry flow fell through to fail_run. Fixes #3231.

packages/core/src/v3/errors.ts

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -320,7 +320,7 @@ export function sanitizeError(error: TaskRunError): TaskRunError {
320320
case "CUSTOM_ERROR": {
321321
// CUSTOM_ERROR.raw holds JSON.stringify(error) which is later parsed by
322322
// JSON.parse in createErrorTaskError. Naive truncation would cut mid-token
323-
// and produce invalid JSON wrap the preview in a valid JSON envelope.
323+
// and produce invalid JSON — wrap the preview in a valid JSON envelope.
324324
const clean = error.raw.replace(/\0/g, "");
325325
const safeRaw =
326326
clean.length > MAX_MESSAGE_LENGTH
@@ -332,7 +332,7 @@ export function sanitizeError(error: TaskRunError): TaskRunError {
332332
};
333333
}
334334
case "INTERNAL_ERROR": {
335-
// message and stackTrace are optional for INTERNAL_ERROR preserve
335+
// message and stackTrace are optional for INTERNAL_ERROR — preserve
336336
// `undefined` so the `error.message ?? "Internal error (CODE)"` fallback
337337
// in createErrorTaskError still kicks in (empty string is not nullish).
338338
return {
@@ -429,6 +429,9 @@ export function shouldLookupRetrySettings(error: TaskRunError): boolean {
429429
case "TASK_RUN_UNCAUGHT_EXCEPTION":
430430
return true;
431431

432+
case "TASK_MIDDLEWARE_ERROR":
433+
return true;
434+
432435
default:
433436
return false;
434437
}
@@ -641,7 +644,7 @@ export class ChatChunkTooLargeError extends Error {
641644
`chat.agent chunk${chunkType ? ` of type "${chunkType}"` : ""} is ${chunkSize} bytes, ` +
642645
`over the realtime stream's per-record cap of ${maxSize} bytes. ` +
643646
`For oversized payloads (e.g. large tool outputs), write the value to your own store and ` +
644-
`emit only an id/url through the chat stream see https://trigger.dev/docs/ai-chat/patterns/large-payloads.`
647+
`emit only an id/url through the chat stream — see https://trigger.dev/docs/ai-chat/patterns/large-payloads.`
645648
);
646649
this.name = "ChatChunkTooLargeError";
647650
}
@@ -744,7 +747,7 @@ const prettyInternalErrors: Partial<
744747
href: links.docs.troubleshooting.stalledExecution,
745748
},
746749
},
747-
// Link only we deliberately do NOT set `message`, so the original
750+
// Link only — we deliberately do NOT set `message`, so the original
748751
// error message (e.g. "read ECONNRESET") is preserved in the dashboard.
749752
// Common cause: an EventEmitter (node-redis, pg, etc.) emitted "error"
750753
// with no listener attached, which Node escalates to uncaughtException.
@@ -1152,7 +1155,7 @@ export function createTaskMetadataFailedErrorStack(
11521155
}
11531156

11541157
stack.push("\n");
1155-
stack.push(` ${taskWithIssues.exportName} in ${taskWithIssues.filePath}`);
1158+
stack.push(` ? ${taskWithIssues.exportName} in ${taskWithIssues.filePath}`);
11561159

11571160
for (const issue of taskWithIssues.issues) {
11581161
if (issue.path) {

packages/core/test/errors.test.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -263,6 +263,12 @@ describe("shouldRetryError + shouldLookupRetrySettings", () => {
263263
expect(shouldLookupRetrySettings(err)).toBe(true);
264264
});
265265

266+
it("retries TASK_MIDDLEWARE_ERROR using the task's retry settings", () => {
267+
const err = internal("TASK_MIDDLEWARE_ERROR");
268+
expect(shouldRetryError(err)).toBe(true);
269+
expect(shouldLookupRetrySettings(err)).toBe(true);
270+
});
271+
266272
it("still does not retry SIGKILL timeout", () => {
267273
expect(shouldRetryError(internal("TASK_PROCESS_SIGKILL_TIMEOUT"))).toBe(false);
268274
});

0 commit comments

Comments
 (0)