Troubleshoot execution leak

An Execution Leak occurs when Lumigo encounters code execution that was leaked from a prior invocation of the serverless function.

This happens when code fails to execute during the lifetime of the invocation that added it to the event loop. This code is only executed in the following invocation of the same container - in other words, the next invocation of the same Lambda function.

Lumigo has identified this as problematic behavior for the following reasons:

  • Security. The concept of isolated environments was violated
  • Non-deterministic behavior. If the Lambda function that leaked this code is never called again before the container is recycled (after enough time passes), then this code is never executed.
1919

This is a common mistake in serverless programming. To help prevent these types of errors from occurring, we’ve provided several examples below of code that will leak execution.

For any additional help, please contact us by clicking the Intercom button in the lower-right-hand corner of your screen, or by emailing us at [email protected].

Node.js

The following asynchronous function resolves before the event loop is empty - not all promises were awaited:

const fn = async () => {
    console.log("may never execute");
};

exports.handler = async (event, context, callback) => {
    console.log("always executes");
    setTimeout(async () => {
        await fn();
    }, 1000);
    return {Hello: 'World'};
};

The following non-asynchronous function executes code after the callback with the environment variable contextcallbackWaitsForEmptyEventLoop set to false:

const fn = async () => {
    console.log("may never execute");
};

exports.handler = (event, context, callback) => {
    context.callbackWaitsForEmptyEventLoop = false;
    console.log("always executes");
    setTimeout(async () => {
        await fn();
    }, 1000);
    callback(null, {Hello: 'World'});
};

Python

In python, any code that executes after a function’s return expression will result in a potential execution leak:

import signal

def fn(*args):
    print("may never execute")

def lambda_handler(event, context):
    print("always executes")
    signal.signal(signal.SIGALRM, fn)
    signal.setitimer(signal.ITIMER_REAL, 1)
    return {"Hello": "World"}