Skip to contentSkip to navigationSkip to topbar
Page toolsOn 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+)