Skip to contentSkip to navigationSkip to topbar
Page tools
Useful for sharing or LLM
Accelerate development with AI

On this page
Looking for more inspiration?Visit the

Migrating Functions from callback to async/await


Starting with Node.js 24, AWS Lambda no longer supports callback-based function handlers. If you're on Twilio's Runtime Handler 2.1.2 or above, your existing callback-style Functions continue to work on all runtimes without code changes. Twilio recommends migrating to async/await regardless.

(information)

Info

Async/await requires Runtime Handler 2.1.2 or above on all runtimes, including Node.js 22. See the Runtime Handler page for how to update.


What is changing

what-is-changing page anchor

Previously, Twilio Functions used a callback pattern to signal completion. To make an async API call, you had to use .then()/.catch() chains:

Callback-style handler (old)

callback-style-handler-old page anchor
1
exports.handler = (context, event, callback) => {
2
const client = context.getTwilioClient();
3
client.messages
4
.create({ to: event.To, from: event.From, body: 'Hello!' })
5
.then((message) => {
6
return callback(null, message.sid);
7
})
8
.catch((error) => {
9
return callback(error);
10
});
11
};

With async/await, you can use await directly inside the handler:

Async/await handler (new)

asyncawait-handler-new page anchor
1
exports.handler = async (context, event, callback) => {
2
const client = context.getTwilioClient();
3
try {
4
const message = await client.messages.create({
5
to: event.To,
6
from: event.From,
7
body: 'Hello!',
8
});
9
return callback(null, message.sid);
10
} catch (error) {
11
return callback(error);
12
}
13
};
(information)

Info

The callback parameter is still available and fully supported. Adding async to your handler signature is the minimum change required — it lets you use await inside your function. You can continue calling callback to return your response, or you can return a value directly and Twilio's runtime-handler will handle it automatically.


Simple synchronous handler

simple-synchronous-handler page anchor

No async operations — just add async to the signature.

1
exports.handler = (context, event, callback) => {
2
const twiml = new Twilio.twiml.MessagingResponse();
3
twiml.message('Hello, World!');
4
return callback(null, twiml);
5
};
1
exports.handler = async (context, event, callback) => {
2
const twiml = new Twilio.twiml.MessagingResponse();
3
twiml.message('Hello, World!');
4
return callback(null, twiml);
5
};

Handler with async operations (.then/.catch)

handler-with-async-operations-thencatch page anchor

Replace promise chains with await and try/catch.

1
exports.handler = (context, event, callback) => {
2
const client = context.getTwilioClient();
3
client.messages
4
.create({ to: event.To, from: event.From, body: 'Hello!' })
5
.then((message) => {
6
return callback(null, message.sid);
7
})
8
.catch((error) => {
9
return callback(error);
10
});
11
};
1
exports.handler = async (context, event, callback) => {
2
const client = context.getTwilioClient();
3
try {
4
const message = await client.messages.create({
5
to: event.To,
6
from: event.From,
7
body: 'Hello!',
8
});
9
return callback(null, message.sid);
10
} catch (error) {
11
return callback(error);
12
}
13
};

Handler returning a value directly

handler-returning-a-value-directly page anchor

With an async handler, you can also return a value directly without calling callback. The runtime-handler detects the returned Promise and handles it automatically.

Returning a value directly

returning-a-value-directly page anchor
1
exports.handler = async (context, event) => {
2
const twiml = new Twilio.twiml.MessagingResponse();
3
twiml.message('Hello, World!');
4
return twiml;
5
};
(warning)

Warning

Do not both call callback and return a value in the same handler. While the runtime-handler guards against double responses, it is still confusing and error-prone. Pick one approach and use it consistently.

Handler with error handling

handler-with-error-handling page anchor

With the callback pattern, errors are passed as the first argument to callback. With async/await you can throw an error instead — if you are returning values directly rather than calling callback.

Error handling with callback (still valid)

error-handling-with-callback-still-valid page anchor
1
exports.handler = async (context, event, callback) => {
2
try {
3
const result = await someAsyncOperation();
4
return callback(null, result);
5
} catch (error) {
6
return callback(error);
7
}
8
};

Error handling by throwing (when returning directly)

error-handling-by-throwing-when-returning-directly page anchor
1
exports.handler = async (context, event, callback) => {
2
const result = await someAsyncOperation(); // throws on failure
3
return result;
4
};

Callback styleAsync/await style
Handler signature(context, event, callback)async (context, event, callback)
Return a responsecallback(null, twiml)callback(null, twiml) or return twiml
Return an errorcallback(error)callback(error) or throw error
Async operations.then().catch()await + try/catch
Node.js 24 supportNoYes (requires runtime-handler 2.1.2+)