Skip to main content

Before you upgrade AWS Lambda Node.js runtime

· 4 min read
Ivan Barlog
AWS Solutions Architect @ BeeSolve

Node.js 24 runtime is available on AWS Lambda from 25th November 2025. It removes support for callback-based function handlers. In this article I will go through things which you should know before you upgrade your runtime.

If you are using callback-based function handlers and you tried to upgrade runtime to Node.js 24 you've probably encountered an error telling you that callback-based function handlers are not supported anymore.

Whenever you use callback-based function handlers the context.callbackWaitsForEmptyEventLoop is set to true by default which essentially means that whenever you forget to await some async function Lambda would still wait for clearing the event loop meaning your code will be executed within single request/response of Lambda. Here is better explanation 1:

callbackWaitsForEmptyEventLoop – By default (true), when using a callback-based function handler, Lambda waits for the event loop to be empty after the callback runs before ending the function invoke. Set to false to send the response and end the invoke immediately after the callback runs instead of waiting for the event loop to be empty. Outstanding events continue to run during the next invocation. Note that Lambda supports callback-based function handlers for Node.js 22 and earlier runtimes only.

Without callbacks and this special setting you need to make sure you will await all your async functions.

Why is this important? Lambda usually don't wait for empty event loop which means that if you return from function handler the Lambda execution stops 2. In practice this means that Lambda freezes and everything what was in event loop is persisted.

Whenever you hit Lambda with another request within 5 minutes when Lambda is being warm the execution continues eg. the frozen state is restored and anything which was previously at the end of event loop is now at start.

This might cause very strange behaviour. Imagine we have following Lambda handler:

import type { APIGatewayProxyEventV2, Context } from "aws-lambda";

type ChatMessage = {
readonly userId: string;
readonly recipientId: string;
readonly message: string;
readonly sentAt: Date;
};

export const handler = async (
event: APIGatewayProxyEventV2,
context: Context,
) => {
const body = parseBody(event.body);
const message: ChatMessage = {
userId: body.userId,
recipientId: body.recipientId,
message: body.message,
sentAt: new Date(),
};

// explicitly missing `await` allowing asynchronous persistence
persistChatMessage(message);

return {
status: 200,
// optimistic response of message before it is persisted
body: JSON.stringify(message),
};
};

function parseBody(
body: APIGatewayProxyEventV2["body"],
): Omit<ChatMessage, "sentAt"> {
// implementation of parsing body with error handling
}

async function persistChatMessage(data: ChatMessage) {
// persist data to the database
}

The above code would work perfectly with callback-based handler but with Node.js 24 the persistChatMessage() won't be executed within the same request/response execution. This particular persistChatMessage() call will be executed on next request if that request comes within 5 minutes window. If no-one hits this particular warm lambda within this window the call won't be executed at all.

When you want to migrate to Node.js 24 you should make sure to go through your codebase and ensure that all async functions are awaited properly. Of course if you know what you are doing you might want to keep this behaviour.

In order to avoid bugs in future you might want to use ESLint require-await rule so no developer forgets their awaits.

What's your experience with migration to Node.js 24? How did you taclked this problem? Did you? Please leave the comment below.

Footnotes

  1. for more information regarding see Using the Lambda context object to retrieve Node.js function information

  2. for more information regarding Lambda lifecycle see Understanding the Lambda execution environment lifecycle