Skip to content
Open
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
41 changes: 41 additions & 0 deletions .changeset/tiny-carrots-rest.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
---
"@trigger.dev/sdk": minor
---

Prevent uncaught errors in the `onSuccess`, `onComplete`, and `onFailure` lifecycle hooks from failing attempts/runs.

Deprecated the `onStart` lifecycle hook (which only fires before the `run` function on the first attempt). Replaced with `onStartAttempt` that fires before the run function on every attempt:

```ts
export const taskWithOnStartAttempt = task({
id: "task-with-on-start-attempt",
onStartAttempt: async ({ payload, ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});

// Default a global lifecycle hook using tasks
tasks.onStartAttempt(({ ctx, payload, task }) => {
console.log(
`Run ${ctx.run.id} started on task ${task} attempt ${ctx.run.attempt.number}`,
ctx.run
);
});
```

If you want to execute code before just the first attempt, you can use the `onStartAttempt` function and check `ctx.run.attempt.number === 1`:

```ts /trigger/on-start-attempt.ts
export const taskWithOnStartAttempt = task({
id: "task-with-on-start-attempt",
onStartAttempt: async ({ payload, ctx }) => {
if (ctx.run.attempt.number === 1) {
console.log("Run started on attempt 1", ctx.run);
}
},
});
```

1 change: 1 addition & 0 deletions apps/webapp/app/components/runs/v3/RunIcon.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -97,6 +97,7 @@ export function RunIcon({ name, className, spanName }: TaskIconProps) {
return <RunFunctionIcon className={cn(className, "text-text-dimmed")} />;
case "task-hook-init":
case "task-hook-onStart":
case "task-hook-onStartAttempt":
case "task-hook-onSuccess":
case "task-hook-onWait":
case "task-hook-onResume":
Expand Down
Binary file modified docs/images/lifecycle-functions.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
258 changes: 174 additions & 84 deletions docs/tasks/overview.mdx
Original file line number Diff line number Diff line change
Expand Up @@ -174,63 +174,14 @@ tasks.onStart(({ ctx, payload, task }) => {

![Lifecycle functions](/images/lifecycle-functions.png)

### `init` function

This function is called before a run attempt:

```ts /trigger/init.ts
export const taskWithInit = task({
id: "task-with-init",
init: async ({ payload, ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});
```

You can also return data from the `init` function that will be available in the params of the `run`, `cleanup`, `onSuccess`, and `onFailure` functions.

```ts /trigger/init-return.ts
export const taskWithInitReturn = task({
id: "task-with-init-return",
init: async ({ payload, ctx }) => {
return { someData: "someValue" };
},
run: async (payload: any, { ctx, init }) => {
console.log(init.someData); // "someValue"
},
});
```

<Info>Errors thrown in the `init` function are ignored.</Info>

### `cleanup` function

This function is called after the `run` function is executed, regardless of whether the run was successful or not. It's useful for cleaning up resources, logging, or other side effects.

```ts /trigger/cleanup.ts
export const taskWithCleanup = task({
id: "task-with-cleanup",
cleanup: async ({ payload, ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});
```

<Info>Errors thrown in the `cleanup` function will fail the attempt.</Info>

### `middleware` and `locals` functions

Our task middleware system runs at the top level, executing before and after all lifecycle hooks. This allows you to wrap the entire task execution lifecycle with custom logic.

<Info>
An error thrown in `middleware` is just like an uncaught error in the run function: it will
propagate through to `catchError()` function and then will fail the attempt (causing a retry).
propagate through to `catchError()` function and then will fail the attempt (either causing a
retry or failing the run).
</Info>

The `locals` API allows you to share data between middleware and hooks.
Expand Down Expand Up @@ -296,14 +247,16 @@ export const myTask = task({
});
```

### `onStart` function
### `onStartAttempt` function

<Info>The `onStartAttempt` function was introduced in v4.1.0</Info>

When a task run starts, the `onStart` function is called. It's useful for sending notifications, logging, and other side effects. This function will only be called one per run (not per retry). If you want to run code before each retry, use the `init` function.
Before a task run attempt starts, the `onStartAttempt` function is called. It's useful for sending notifications, logging, and other side effects.

```ts /trigger/on-start.ts
export const taskWithOnStart = task({
id: "task-with-on-start",
onStart: async ({ payload, ctx }) => {
export const taskWithOnStartAttempt = task({
id: "task-with-on-start-attempt",
onStartAttempt: async ({ payload, ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
Expand All @@ -312,20 +265,33 @@ export const taskWithOnStart = task({
});
```

You can also define an `onStart` function in your `trigger.config.ts` file to get notified when any task starts.
You can also define a global `onStartAttempt` function using `tasks.onStartAttempt()`.

```ts trigger.config.ts
import { defineConfig } from "@trigger.dev/sdk";
```ts init.ts
import { tasks } from "@trigger.dev/sdk";

export default defineConfig({
project: "proj_1234",
onStart: async ({ payload, ctx }) => {
console.log("Task started", ctx.task.id);
},
tasks.onStartAttempt(({ ctx, payload, task }) => {
console.log(
`Run ${ctx.run.id} started on task ${task} attempt ${ctx.run.attempt.number}`,
ctx.run
);
});
```

<Info>Errors thrown in the `onStart` function are ignored.</Info>
<Info>Errors thrown in the `onStartAttempt` function will cause the attempt to fail.</Info>

If you want to execute code before just the first attempt, you can use the `onStartAttempt` function and check `ctx.run.attempt.number === 1`:

```ts /trigger/on-start-attempt.ts
export const taskWithOnStartAttempt = task({
id: "task-with-on-start-attempt",
onStartAttempt: async ({ payload, ctx }) => {
if (ctx.run.attempt.number === 1) {
console.log("Run started on attempt 1", ctx.run);
}
},
});
```

### `onWait` and `onResume` functions

Expand All @@ -350,6 +316,20 @@ export const myTask = task({
});
```

You can also define global `onWait` and `onResume` functions using `tasks.onWait()` and `tasks.onResume()`:

```ts init.ts
import { tasks } from "@trigger.dev/sdk";

tasks.onWait(({ ctx, payload, wait, task }) => {
console.log("Run paused", ctx.run, wait);
});

tasks.onResume(({ ctx, payload, wait, task }) => {
console.log("Run resumed", ctx.run, wait);
});
```

### `onSuccess` function

When a task run succeeds, the `onSuccess` function is called. It's useful for sending notifications, logging, syncing state to your database, or other side effects.
Expand All @@ -366,20 +346,20 @@ export const taskWithOnSuccess = task({
});
```

You can also define an `onSuccess` function in your `trigger.config.ts` file to get notified when any task succeeds.
You can also define a global `onSuccess` function using `tasks.onSuccess()`.

```ts trigger.config.ts
import { defineConfig } from "@trigger.dev/sdk";
```ts init.ts
import { tasks } from "@trigger.dev/sdk";

export default defineConfig({
project: "proj_1234",
onSuccess: async ({ payload, output, ctx }) => {
console.log("Task succeeded", ctx.task.id);
},
tasks.onSuccess(({ ctx, payload, output }) => {
console.log("Task succeeded", ctx.task.id);
});
```

<Info>Errors thrown in the `onSuccess` function are ignored.</Info>
<Info>
Errors thrown in the `onSuccess` function will be ignored, but you will still be able to see them
in the dashboard.
</Info>

### `onComplete` function

Expand All @@ -397,6 +377,21 @@ export const taskWithOnComplete = task({
});
```

You can also define a global `onComplete` function using `tasks.onComplete()`.

```ts init.ts
import { tasks } from "@trigger.dev/sdk";

tasks.onComplete(({ ctx, payload, output }) => {
console.log("Task completed", ctx.task.id);
});
```

<Info>
Errors thrown in the `onComplete` function will be ignored, but you will still be able to see them
in the dashboard.
</Info>

### `onFailure` function

When a task run fails, the `onFailure` function is called. It's useful for sending notifications, logging, or other side effects. It will only be executed once the task run has exhausted all its retries.
Expand All @@ -413,20 +408,20 @@ export const taskWithOnFailure = task({
});
```

You can also define an `onFailure` function in your `trigger.config.ts` file to get notified when any task fails.
You can also define a global `onFailure` function using `tasks.onFailure()`.

```ts trigger.config.ts
import { defineConfig } from "@trigger.dev/sdk";
```ts init.ts
import { tasks } from "@trigger.dev/sdk";

export default defineConfig({
project: "proj_1234",
onFailure: async ({ payload, error, ctx }) => {
console.log("Task failed", ctx.task.id);
},
tasks.onFailure(({ ctx, payload, error }) => {
console.log("Task failed", ctx.task.id);
});
```

<Info>Errors thrown in the `onFailure` function are ignored.</Info>
<Info>
Errors thrown in the `onFailure` function will be ignored, but you will still be able to see them
in the dashboard.
</Info>

<Note>
`onFailure` doesn’t fire for some of the run statuses like `Crashed`, `System failures`, and
Expand All @@ -441,7 +436,7 @@ Read more about `catchError` in our [Errors and Retrying guide](/errors-retrying

<Info>Uncaught errors will throw a special internal error of the type `HANDLE_ERROR_ERROR`.</Info>

### onCancel
### `onCancel` function

You can define an `onCancel` hook that is called when a run is cancelled. This is useful if you want to clean up any resources that were allocated for the run.

Expand Down Expand Up @@ -540,6 +535,101 @@ export const cancelExampleTask = task({
point the process will be killed.
</Note>

### `onStart` function (deprecated)

<Info>The `onStart` function was deprecated in v4.1.0. Use `onStartAttempt` instead.</Info>

When a task run starts, the `onStart` function is called. It's useful for sending notifications, logging, and other side effects.

<Warning>
This function will only be called once per run (not per attempt). If you want to run code before
each attempt, use a middleware function or the `onStartAttempt` function.
</Warning>

```ts /trigger/on-start.ts
export const taskWithOnStart = task({
id: "task-with-on-start",
onStart: async ({ payload, ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});
```

You can also define a global `onStart` function using `tasks.onStart()`.

```ts init.ts
import { tasks } from "@trigger.dev/sdk";

tasks.onStart(({ ctx, payload, task }) => {
console.log(`Run ${ctx.run.id} started on task ${task}`, ctx.run);
});
```

<Info>Errors thrown in the `onStart` function will cause the attempt to fail.</Info>

### `init` function (deprecated)

<Warning>
The `init` hook is deprecated and will be removed in the future. Use
[middleware](/tasks/overview#middleware-and-locals-functions) instead.
</Warning>

This function is called before a run attempt:

```ts /trigger/init.ts
export const taskWithInit = task({
id: "task-with-init",
init: async ({ payload, ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});
```

You can also return data from the `init` function that will be available in the params of the `run`, `cleanup`, `onSuccess`, and `onFailure` functions.

```ts /trigger/init-return.ts
export const taskWithInitReturn = task({
id: "task-with-init-return",
init: async ({ payload, ctx }) => {
return { someData: "someValue" };
},
run: async (payload: any, { ctx, init }) => {
console.log(init.someData); // "someValue"
},
});
```

<Info>Errors thrown in the `init` function will cause the attempt to fail.</Info>

### `cleanup` function (deprecated)

<Warning>
The `cleanup` hook is deprecated and will be removed in the future. Use
[middleware](/tasks/overview#middleware-and-locals-functions) instead.
</Warning>

This function is called after the `run` function is executed, regardless of whether the run was successful or not. It's useful for cleaning up resources, logging, or other side effects.

```ts /trigger/cleanup.ts
export const taskWithCleanup = task({
id: "task-with-cleanup",
cleanup: async ({ payload, ctx }) => {
//...
},
run: async (payload: any, { ctx }) => {
//...
},
});
```

<Info>Errors thrown in the `cleanup` function will cause the attempt to fail.</Info>

## Next steps

<CardGroup>
Expand Down
Loading
Loading