---
title: "io.runTask()"
sidebarTitle: "runTask()"
description: "Creates and runs a Task inside a Run."
---

A [Task](/documentation/concepts/tasks) is a cached unit of work in a Job Run that are logged to the Trigger.dev UI. You can use `io.runTask()` to create and run a Task inside a Run.

<Warning>
  Any interaction with an external service (database or API) should be wrapped in a Task. Failing to
  do so could result in repeated work when runs are resumed.
</Warning>

## Parameters

<Snippet file="stable-key-param.mdx" />

<ResponseField name="callback" type="function" required>
  The callback that will be called when the Task is run, this is where your logic should go. The callback receives
  the Task and the IO as parameters.

  <Expandable title="arguments">
    <ResponseField name="task" type="Task">
      The Task that is running. It has some useful properties like `idempotencyKey` and `attempts`.
    </ResponseField>
    <ResponseField name="io" type="IO">
      [IO](/sdk/io/overview) holds Integrations and useful actions you can perform.
    </ResponseField>
  </Expandable>
</ResponseField>

<ResponseField name="options" type="object">
  The options of how you'd like to run and log the Task.

<Expandable title="options">
  <ResponseField name="name" type="string">
    This is displayed on the Task in the logs.
  </ResponseField>
  <ResponseField name="delayUntil" type="date">
    The Task will wait and only start at the specified Date.
  </ResponseField>
  <ResponseField name="retry" type="object">
    Retry options.

    <Expandable title="options">
      <ResponseField name="limit" type="number">
        The maximum number of times to retry the request.
      </ResponseField>
      <ResponseField name="minTimeoutInMs" type="number">
        The minimum amount of time to wait before retrying the request.
      </ResponseField>
      <ResponseField name="maxTimeoutInMs" type="number">
        The maximum amount of time to wait before retrying the request.
      </ResponseField>
      <ResponseField name="factor" type="number">
        The exponential factor to use when calculating the next retry time.
      </ResponseField>
      <ResponseField name="randomize" type="boolean">
        Whether to randomize the retry time.
      </ResponseField>
    </Expandable>

  </ResponseField>
  <ResponseField name="icon" type="string">
    The icon for the Task, it will appear in the logs. You can use the name of a
    company in lowercase, e.g. "github". Or any icon name that [Tabler Icons](https://tabler-icons.io/) supports.
  </ResponseField>
  <ResponseField name="description" type="string">
    A description of the Task.
  </ResponseField>
    <ResponseField name="properties" type="array">
      Properties that are displayed in the logs.

      Each property has the following fields:

      <Expandable title="property fields">
        <ResponseField name="label" type="string" required>
          The label for the property.
        </ResponseField>
        <ResponseField name="text" type="string" required>
          The value of the property.
        </ResponseField>
        <ResponseField name="url" type="string">
          The URL to link to when the property is clicked.
        </ResponseField>
      </Expandable>
    </ResponseField>

{" "}

<ResponseField name="params" type="any">
  The input params to the Task, will be displayed in the logs.
</ResponseField>

  <ResponseField name="style" type="object">
    The style of the log entry.

    <Expandable title="style">
      <ResponseField name="style" type="string">
        The style: `normal` or `minimal`.
      </ResponseField>
      <ResponseField name="variant" type="string">
        A variant of the style.
      </ResponseField>
    </Expandable>

  </ResponseField>

  <ResponseField name="redact" type="RedactOptions">
    An optional object that specifies which fields to redact from the logs. This is useful for sensitive data like API keys.

    <Expandable title="redact" defaultOpen>
      <ResponseField name="paths" type="string[]">
        An array of paths to redact. A path is a dot separated string, e.g. `user.email`. Currently does not support wildcards.
      </ResponseField>
    </Expandable>

  </ResponseField>

  <ResponseField name="callback" type="object">
    An optional object that exposes settings for the remote callback feature.

    Enabling this feature will expose a `callbackUrl` property on the callback's Task parameter. Additionally, `io.runTask()` will now return a Promise that resolves with the body of the first request sent to that URL.

    <Expandable title="property fields">
      <ResponseField name="enabled" type="boolean" required>
        Whether to enable the remote callback feature.
      </ResponseField>
      <ResponseField name="timeoutInSeconds" type="number" required>
        The value of the property.
      </ResponseField>
    </Expandable>

  </ResponseField>

</Expandable>
</ResponseField>

<ResponseField name="onError" type="function">
  An optional callback that will be called when the Task fails. You can perform
  logic in here and optionally return a custom error object. Returning an object with `{ retryAt: Date, error?: Error }` will retry the Task at the specified Date. You can also just return a new `Error` object to throw a new error. Returning `null` or `undefined` will rethrow the original error. If you want to force retrying to be skipped, return `{ skipRetrying: true }`.

  <Expandable title="arguments">
    <ResponseField name="error" type="unknown">
      The underlying error that was thrown inside the Task callback.
    </ResponseField>
    <ResponseField name="task" type="Task">
      The Task that failed.
    </ResponseField>
  </Expandable>
</ResponseField>

## Returns

A Promise that resolves with the returned value of the callback.

If the remote callback feature `options.callback` is enabled, the Promise will instead resolve with the body of the first request sent to `task.callbackUrl`.

<RequestExample>

```typescript Run a task
client.defineJob({
  id: "alert-on-new-github-issues",
  name: "Alert on new GitHub issues",
  version: "1.0.0",
  trigger: github.triggers.repo({
    event: events.onIssueOpened,
    owner: "triggerdotdev",
    repo: "trigger.dev",
  }),
  run: async (payload, io, ctx) => {
    const record = await io.runTask(
      "sync-github-issue",
      async (task) => {
        return prisma.githubIssues.create({
          data: {
            number: payload.issue.number,
            title: payload.issue.title,
            body: payload.issue.body,
            url: payload.issue.html_url,
            repo: payload.repository.full_name,
            owner: payload.repository.owner.login,
          },
        });
      },
      //this is optional
      { name: "Sync GitHub Issue", icon: "github" }
    );
  },
});
```

```typescript onError callback
client.defineJob({
  id: "custom-error-handling",
  name: "Custom Error handling",
  version: "0.1.1",
  trigger: eventTrigger({ name: "test" }),
  run: async (payload, io, ctx) => {
    //runTask
    const response = await io.runTask(
      "on-error-test",
      async () => {
        // Only throw error 50% of the time
        if (Math.random() > 0.5) {
          throw new Error("Something went wrong");
        }
      },
      { name: "On Error" },
      (error, task) => {
        //retry the task in 5 minutes
        return {
          retryAt: new Date(Date.now() + 5 * 60 * 1000),
          error: new Error("Something went REALLY wrong")
        };
      }
    );

    //log the url of the created card
    await io.logger.info(response.data.url);
  },
});
```

```typescript Remote callbacks
client.defineJob({
  id: "remote-callback-example",
  name: "Remote Callback example",
  version: "0.1.1",
  trigger: eventTrigger({ name: "predict" }),
  run: async (payload, io, ctx) => {
    const prediction = await io.runTask(
      "create-and-await-prediction",
      async (task) => {
        //create a prediction using a Replicate SDK instance
        await replicate.predictions.create({
          ...payload,
          webhook: task.callbackUrl ?? "",
          webhook_events_filter: ["completed"],
        });
        //the actual return value will be the data sent to callbackUrl
        //cast to the exact data type you expect to receive or `any` if unsure
        return {} as Prediction;
      },
      {
        name: "Create and await Prediction",
        icon: "replicate",
        //remote callback settings
        callback: {
          enabled: true,
          timeoutInSeconds: 300,
        },
      }
    );

    //log the prediction output
    await io.logger.info(prediction.output);
  },
});
```

</RequestExample>
