# Functions and Assets FAQ

## Why does my Function return 'runtime application timed out'?

There are two likely reasons your Function has completed with the error: 'runtime application timed out'.

The most common reason is that your Function has exceeded the 10-second execution time limit. You can determine this by looking at the execution logs on the Function instance page. The last log line after execution will tell you how many milliseconds the Function took to execute. If the processing time is greater than 10,000 milliseconds, then Twilio terminated your Function.

![Log showing runtime application timed out after 11012.55ms using 110MB.](https://docs-resources.prod.twilio.com/82d12c47936cecc1b7e0d9c9d8e06c255b8669384644f78ec9e3bbd647a96a57.png)

The other, more subtle reason your Function ended with an application timeout is because of an incorrect invocation of `callback()`. If your Function does not call the `callback()` method or the method is unreachable, your Function will continue executing until it reaches the time limit and ultimately fails. A very common mistake is to forget to capture the `catch()` rejected state of a `Promise` and calling `callback()` there as well. The [Function Execution documentation](/docs/serverless/functions-assets/functions/invocation#callback-function) provides extensive details on the functionality and usage of the `callback()` method. Below are several examples of correct use of `callback()` to complete execution and emit a response.

```js title="Complete Execution with Asynchronous HTTP Request" description="Example of how to appropriately use callback() with an asynchronous HTTP request"
exports.handler = (context, event, callback) => {
  // Fetch the already initialized Twilio REST client
  const client = context.getTwilioClient();

  // Determine message details from the incoming event, with fallback values
  const from = event.From || '+15095550100';
  const to = event.To || '+15105550101';
  const body = event.Body || 'Ahoy, World!';

  client.messages
    .create({ to, from, body })
    .then((result) => {
      console.log('Created message using callback');
      console.log(result.sid);
      // Callback is placed inside the successful response of the request
      return callback();
    })
    .catch((error) => {
      console.error(error);
      // Callback is also used in the catch handler to end execution on errors
      return callback(error);
    });

 // If you were to place the callback() function here, instead, then the process would
 // terminate before your API request to create a message could complete.
};
```

```js title="Return a Simple Successful Response" description="Example of how to return an empty HTTP 200 OK"
exports.handler = (context, event, callback) => {
  // Providing neither error nor response will result in a 200 OK
  return callback();
};
```

## Why isn't my code running?

The most common reason we have seen that a Function appears not to run is the misuse of `callback`. Your Function invocation terminates when it calls `callback`. If your request is asynchronous, for example, an API call to a Twilio resource, then you must call `callback` *after* the success response of the request has resolved. This would be in the `then`/`catch` blocks chained to a request, or after a request that uses the `await` keyword in an `async` context.

## Can API Calls occur after a Function Execution has ended?

Yes! Outgoing API requests from inside a Function (either directly via `axios`/`got`, or through an SDK such as Twilio's) can still be in an enqueued state even after the execution of a Function has ended.

This happens if you make an asynchronous request that returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), and forget to either `await` it or chain on a `.then` handler. Here, the Function may finish execution before the request occurs or completes. Then, your request may be deferred, sit idle, and run on a subsequent Function invocation in your Service before our system cleans it up.

### How do I keep this from happening?

Always be sure to wait for the Promises generated by your API calls and methods to resolve before invoking `callback`. Below are examples of improperly and properly handled asynchronous logic, one which uses `.then` chaining, and another using `async`/`await` syntax.

```js
// An example of an improperly handled Promise
exports.handler = (context, event, callback) => {
  const client = context.getTwilioClient();
  client.messages.create({
    body: "hi",
    to: toNumber,
    from: fromNumber,
  });
  return callback(null, 'success');
};
```

#### Using `.then` chaining

```js
// An example of properly waiting for message creation to
// finish before ending Function execution
exports.handler = (context, event, callback) => {
  const client = context.getTwilioClient();
  client.messages
    .create({
      body: 'hi',
      to: event.toNumber,
      from: event.fromNumber,
    })
    .then((message) => {
      return callback(null, `Success! Message SID: ${message.sid}`);
    });
    // Ideally, you would also have some .catch logic to handle errors
};

```

#### Using `async`/`await`

```js
// An example of properly waiting for message creation to
// finish before ending Function execution
exports.handler = async (context, event, callback) => {
  const client = context.getTwilioClient();
  const message = await client.messages.create({
    body: 'hi',
    to: event.toNumber,
    from: event.fromNumber,
  });

  return callback(null, `Success! Message SID: ${message.sid}`);
  // Ideally, you would add some try/catch logic to handle errors
};
```

## How do I construct Voice TwiML?

You can generate Voice TwiML using the [Twilio Node library,](https://github.com/twilio/twilio-node) which comes packaged within your Function.

Construct Voice TwiML

```js
exports.handler = (context, event, callback) => {
  const twiml = new Twilio.twiml.VoiceResponse()
  twiml.dial().sip("sip:jack@example.com")
  return callback(null, twiml);
}
```

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Dial>
    <Sip>sip:jack@example.com</Sip>
  </Dial
</Response>
```

## How do I construct Messaging TwiML?

You can generate Messaging TwiML using the [Twilio Node library](https://github.com/twilio/twilio-node), which comes packaged within your Function.

Construct Messaging TwiML

```js
exports.handler = (context, event, callback) => {
  const twiml = new Twilio.twiml.MessagingResponse();
  twiml.message('The Robots are coming! Head for the hills!');
  callback(null, twiml);
}
```

```xml
<?xml version="1.0" encoding="UTF-8"?>
<Response>
  <Message>The Robots are coming! Head for the hills!</Message>
</Response>
```

## How do I return JSON?

Simply return your object as outlined in the [Function Execution documentation](/docs/serverless/functions-assets/functions/invocation#return-a-successful-json-response) or the following sample code:

```js title="Return a Successful JSON Response" description="Example of how to return JSON in HTTP 200 OK"
exports.handler = (context, event, callback) => {
  // Construct an object in any way
  const response = { result: 'winner winner!' };
  // A JavaScript object as a response will be serialized to JSON and returned
  // with the Content-Type header set to "application/json" automatically
  return callback(null, response);
};
```

## Can I interact with the OPTIONS request?

No, you cannot interact with the [pre-flight OPTIONS request](https://developer.mozilla.org/en-US/docs/Glossary/Preflight_request) that browsers send.

The Runtime client will automatically respond to `OPTIONS` requests with the following values:

```bash
Access-Control-Allow-Origin: *
Access-Control-Allow-Headers: *
Access-Control-Allow-Methods: GET, POST, OPTIONS

```

This means that all origins may access your Function, all headers are passed through, and that the only methods allowed to access your Function are `GET`, `POST`, and OPTIONS.

## How do I send CORS headers?

You can send CORS headers by using the Twilio Response object described in [this example](/docs/serverless/functions-assets/quickstart/enabling-cors-between-flex-plugins-and-functions#enable-cors).

## How can I use an ES Module in my Function?

If you've tried to use a package such as `got` or `p-retry` in your Functions lately, you may have seen this error in your logs:

```bash
Error [ERR_REQUIRE_ESM]: Must use import to load ES Module: /Users/your-name/your-project/node_modules/got/dist/source/index.js
  require() of ES modules is not supported.
  require() of /Users/your-name/your-project/node_modules/got/dist/source/index.js from /Users/your-name/your-project/functions/retry.js is an ES module file as it is a .js file whose nearest parent package.json contains "type": "module" which defines all .js files in that package scope as ES modules.
  Instead rename index.js to end in .cjs, change the requiring code to use import(), or remove "type": "module" from /Users/your-name/your-project/node_modules/got/package.json.

      at new NodeError (internal/errors.js:322:7)
      at Object.Module._extensions..js (internal/modules/cjs/loader.js:1102:13)
      at Module.load (internal/modules/cjs/loader.js:950:32)
      at Function.Module._load (internal/modules/cjs/loader.js:790:12)
      at Module.require (internal/modules/cjs/loader.js:974:19)
      at require (internal/modules/cjs/helpers.js:101:18)
      at Object. (/Users/your-name/your-project/functions/retry.js:2:13)
      at Module._compile (internal/modules/cjs/loader.js:1085:14)
      at Object.Module._extensions..js (internal/modules/cjs/loader.js:1114:10)
      at Module.load (internal/modules/cjs/loader.js:950:32)
```

This stems from the fact that packages in the Node.js ecosystem are migrating over from the old CommonJS (CJS) standard to the newer, ES Modules (ESM) standard. You can read about the differences in far more detail in [this blog post](https://redfin.engineering/node-modules-at-war-why-commonjs-and-es-modules-cant-get-along-9617135eeca1).

At the moment, Functions only support CJS, but many packages such as `p-retry` that you might want to use are now only exported as ESM. You *could* get around this by pinning to an older version of the library that is still CJS, but that opens you up to vulnerabilities and old, unsupported dependencies.

A better solution is to leverage [dynamic imports](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Statements/import#dynamic_imports), which allow you to import and run code from an ESM package even if your Function is still in CJS.

Using the `import` method in this scenario returns a [Promise](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Promise), so you will need to properly handle that promise before running the rest of your Function's code. You can do so by nesting your Function's logic within a `.then` chain, like so:

```javascript
exports.handler = (context, event, callback) => {
  import('got').then(({ default: got }) => {
    got('https://httpbin.org/anything')
      .json()
      .then((response) => {
        // ... the rest of your Function logic
      });
  });
};
```

However, it's recommended to leverage [async/await syntax](https://developer.mozilla.org/en-US/docs/Learn/JavaScript/Asynchronous/Async_await) to handle the promise and minimize the amount of chaining/nesting necessary in your Function:

```javascript
exports.handler = async (context, event, callback) => {
  const { default: got } = await import('got');

  const response = await got('https://httpbin.org/anything').json();
  // ... the rest of your Function logic
};
```

Note in the above examples that you are [destructuring](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Destructuring_assignment) the default export from the `got` package, and renaming it to the intended name you'd use when importing it with a static `require` statement. Refer to this guide on [API Calls](/docs/serverless/functions-assets/quickstart/api-request#configure-retries-for-an-api-request) to see a full example of a Function using dynamic imports with an ESM package.

You can use this same strategy for packages that export multiple, named methods:

```javascript
const { getPhoneNumbers, ahoy } = await import('some-package');
```

Lastly, you can import *all* methods from a package to a single variable, and access them by name like so:

```javascript
const somePackage = await import('some-package');

somePackage.default(); // Runs the default export from 'some-package'
somePackage.getPhoneNumbers();
somePackage.ahoy('hoy');
```

## Can I execute my Functions on a schedule?

Currently, Functions are event-driven and can only be invoked by HTTP.

## Can I see examples of existing Functions?

Absolutely! You can find a variety of examples in the navigation bar as well as on our [Function Examples page](/docs/serverless/functions-assets/quickstart).

## How many Functions can I create?

During the Public Beta period, the UI editor can load and present up to 100 Functions or Assets by default. If a service has over 100 Functions or Assets, click on **Load more** to view the remaining resources, or the next 100 if there are more than that.

The **Deploy All** and **Save** buttons, and the ability to change Dependencies, will be disabled until all the Functions and Assets in the service have been loaded. Similarly, it is not possible to delete Functions or Assets until all the Functions or Assets are loaded for view.

To create and manage more Functions and/or Assets, we suggest using the [Serverless Toolkit](/docs/labs/serverless-toolkit) or the [Functions and Assets API](/docs/serverless/api) directly.

## What limits are in place on Function invocation?

We limit Functions to 30 concurrent invocations — meaning that if you have over 30 Functions being invoked at the same time, you will see new Function invocations return with a 429 status code. To keep below the threshold, optimize your Functions to return as fast as possible — avoid artificial timeouts and use asynchronous calls to external systems rather than waiting on many API calls to complete.

## How can I monitor how many Twilio Functions are running concurrently?

Twilio Functions include a response header called `x-twilio-function-concurrency`, which shows the number of concurrent function invocations at the time Twilio sends the response. This gives you real-time visibility into how many functions are executing simultaneously.

Twilio enforces a limit of 30 concurrent invocations on your account. If this limit is exceeded, any new Function invocations will return a HTTP 429 (Too Many Requests) status code. Monitoring the x-twilio-function-concurrency header allows you to proactively manage your workloads to stay below this threshold, helping you avoid unexpected 429 errors and ensuring your applications remain resilient under load.

```bash
x-twilio-function-concurrency: 1
```

## Can I create temporary files when executing a Function?

Yes. See [this blog post](https://www.twilio.com/blog/temporary-storage-twilio-functions) for a description and samples on how to store files for one-off usage.

## Are there any Twilio resources for learning Node.js?

* Twilio Quest for JavaScript
* \#javascript public Slack channel in [Twilio Quest Slack](https://app.slack.com/client/TAKM9J7ND/CJDL29HJB)
* Blog Posts around Asynchronous JavaScript techniques

  * [5 Ways to Make HTTP Requests in Node.js using Async/Await](https://www.twilio.com/blog/5-ways-to-make-http-requests-in-node-js-using-async-await)
  * [Async/Await: The Hero JavaScript Deserved](https://www.twilio.com/blog/asyncawait-the-hero-javascript-deserved.html)
  * [Asynchronous JavaScript: Introducing async and await](https://www.twilio.com/blog/asynchronous-javascript-introducing-async-and-await)
  * [Using RxJS Observables With JavaScript Async and Await](https://www.twilio.com/blog/using-rxjs-observables-with-javascript-async-and-await)
  * [Asynchronous JavaScript: Using RxJS Observables with REST APIs in Node.js](https://www.twilio.com/blog/async-js-rxjs-observables-rest-api-nodejs)
  * [Asynchronous JavaScript: Organizing Callbacks for Readability and Reusability](https://www.twilio.com/blog/asynchronous-javascript-organize-callbacks-readability-reusability)
  * [Asynchronous JavaScript: Refactoring Callbacks to Promises in Node.js](https://www.twilio.com/blog/asynchronous-javascript-refactor-callbacks-promises-node-js)

## Which runtimes are available for Twilio Functions?

The following Node.js runtimes are currently available for Twilio Functions:

| `runtime` | Version         | Status    | Comments                      |
| --------- | --------------- | --------- | ----------------------------- |
| `node20`  | Node.js 20.18.3 | available | Available until June 11, 2026 |
| `node22`  | Node.js 22.14.0 | available | Default since June 1, 2025    |

> \[!NOTE]
>
> The `runtime` value is a parameter that you can provide at build time. See an example on [how to pass this parameter](/docs/serverless/api/resource/build#code-create-a-build-with-a-specific-nodejs-runtime) for more context.
