Sh#! happens. Errors occur. The network slows down, memory runs out, an external resource becomes unavailable. Maybe there’s a security issue.

Whatever the case may be, an API must be able to clearly identify the error, catch it, and give you the tools to manage the error—ideally, programmatically and in real-time.

In this guide, you’ll learn how to use Defer to manage the following kinds of errors:

  • Timeouts
  • External failures
  • Runtime errors
  • Internal errors

Filter executions for a given Error Reason

You can always go to the Defer Console to quickly identify errors that impact your application. The Defer Console’s Error Reason filter allows you to analyze errors by error type:

Error reason filter

First, select the metadata filter and Error reason

Error reason

Finally, select Error reason

Filtered executions

You can now inspect executions for the select Error reason

How to handle timeouts

Unwanted timeouts due to external factors, such as network or 3rd party API outages, can compromise your concurrency capabilities. Here are two patterns that mitigate timeouts:

1 - Timeout control with the maxDuration option

Since @defer/client@1.6.0, you can specify a maxDuration to avoid unwanted timeouts.

const importContacts = (companyId: string, contacts: Contact[]) => {
  //  ...

export default defer(importContacts, {
  maxDuration: 10, // will timeout after 10secs

2 - Canceling executions

Since @defer/client@1.8.0, you can also cancel pending executions from the API.

import { cancelExecution } from "@defer/client";

// ...

const { id } = await cancelExecution(executionId);

// ...

Note: You can also cancel pending and running executions from the Defer Console.

How to handle runtime and external errors

External errors are linked to 3rd party API outages or network issues, while runtime errors range from typos and malformed data to memory issues. Both can be mitigated with the retry option and by enabling Slack notifications.

Here’s an overview of Defer’s retry capabilities. For a full discussion with use case and notification examples, please read our Deep Dive into Defer’s Retry Strategies.

Standard Retry strategies

Defer defaults to no retry:

import { defer } from "@defer/client";

async function myBgFunction() {
  /* ... */

export default defer(myBgFunction);
// is equivalent to:
export default defer(myBgFunction, { retry: false });

Adding { retry: true } to your background function will enable the retry option, which defaults up to 13 retries.

You can also control the amount of retries by replacing true with a number, such as { retry: 5 }.

export default defer(myBgFunction, { retry: 5 });

The retry combined with concurrency is a simple way to deal with API rate-limiting.

For example, the below function—running for 500ms—will ensure that the 3rd party API will be called only once per second:

export default defer(myBgFunction, {
  retry: 5,
  concurrency: 2,

The { retry: false }, { retry: true }, { retry: 5 } are shorthands to disable or configure retries.

However, Defer offers more precise retry strategies.

Fine-tuning your retry strategy

Defer comes with a number of retry parameters. The code below shows you the role of each retry option:

   // How many retries should we attempt in total?
   maxAttempts: 3,

   // How close the first retry should be? (in seconds) ?
   initialInterval: 30,

   // How much randomness should be introduced in the space between retries?
   randomizationFactor: 0,5,

   // How fast the space between each retry should grow? (exponential back-off)
   multiplier: 1,5,

   // What should be the max space between 2 retries? (in seconds)
   maxInterval: 600,

Again, take a look at our retry deep dive for a full discussion and more use cases.