Debugging with Dashbird: AWS Lambda Process Exited Before Completing Request

Another generic error message from our favorite FaaS provider AWS Lambda. 

And again, there are multiple reasons why this issue could arise. Let’s first look at the basics of AWS Lambda to get a better intuition for when things go wrong later.

AWS Lambda is Asynchronous

Lambda is an asynchronous event-based service at heart. The Lambda service asynchronously calls your functions—this doesn’t mean that all events are handled asynchronously, but relates mainly to the programming model you have to use inside your function.

This shouldn’t be confused with the event handling of Lambda, which can be synchronous and asynchronous.

In synchronous event handling, for example, when your function handles an API Gateway event, the event provider waits until you handled the event so that it can supply its own client with your results.

A browser sends a request to API Gateway, and API Gateway, in turn, sends the request as an event to a Lambda function. The browser will now wait for a response, and API Gateway will wait to handle the event. The longer your function takes, the longer both the browser and API Gateway will wait. Until a maximum of 30 seconds, API Gateway will call it a day and tell the browser your function timed out.

In asynchronous event handling, the event provider will just give the event to Lambda and call it a day. It will not wait for an answer from your Lambda function, whether it takes three seconds or ten minutes.

If you trigger a Lambda function from an S3 upload, the S3 service will never notice if the Lambda function failed or succeeded in handling the event. It will just trigger the function and then march on to other tasks.

Asynchronous Event Handling

Inside your Lambda function, you have to write asynchronous code. You can’t simply take some arguments and return your calculations directly, like in the following example.

exports.handler = (event, context) => {
  const result = event.data.x * event.data.x;
  return { statusCode: 200, body: result };
});

You have to use some asynchronous computing constructs to handle the results. In the currently supported versions of the Node.js runtime for AWS Lambda, you have to use promises or asynchronous functions to handle your return values. 

A promise version would look like this:

exports.handler =(event, context) => new Promise((resolve, reject) => {
  const result = event.data.x * event.data.x;
  resolve({ statusCode: 200, body: result });
});

Your handler function would directly return a new promise object that would be resolved in the future. You would call resolve if all went well or reject if you encountered an error.

And the corresponding asynchronous function looks like that:

exports.handler = async (event, context) => {
  const result = event.data.x * event.data.x;
  return { statusCode: 200, body: result };
});

An asynchronous function is a regular function marked with async at the beginning. This keyword wraps the whole function in a promise, so you don’t have to do it manually. If you return, the promise will be resolved. If you throw an error, the promise will be rejected. This way, you can write code that looks synchronous but behaves asynchronously.

Using an asynchronous function is a “de facto” standard of writing Lambda handlers.

The Legacy Way

When Node.js version 11 was still supported, you had to use the context or a callback for your returns.

The context version looked like this:

exports.handler = (event, context) => {
  const result = event.data.x * event.data.x;
  context.done(null, { statusCode: 200, body: result });
});

Here the first argument to context.done is an error object; if none exist, we can use null.

The callback version looked like that:

exports.handler = (event, context, callback) => {
  const result = event.data.x * event.data.x;
  callback({ statusCode: 200, body: result });
});

As you can see, the asynchronous event handling got streamlined within new Node.js versions, so you only have to return from an asynchronous function, and Node.js will take care of the rest.

What Does Completing a Request Mean?

Now that we have a basic understanding of how AWS Lambda works let’s look at our error.

AWS Lambda process exited before completing request

In the above code examples, we saw how it looks when we complete a request, so you might know what will cause the error to happen.

In the legacy code that uses context.done or the callback, this error will happen when your code doesn’t reach one of these calls.

In the current versions that use promises or asynchronous functions, this error will happen if the promise didn’t resolve or the function didn’t return.

So, your function code did all of its work but never reached one of these four end states.

An asynchronous function implicitly returns if no code is left to work on, so even if you don’t write a return statement, it will still “complete” just without any returned values. The other three versions of completing require you to call something explicitly, be it context.done, callback, or resolve.

What Leads to This Error?

This error is mostly related to legacy behavior. These days other errors trigger instead of this one. So, this error is mostly a “catch what’s left” kind of error.

There are multiple reasons why you didn’t call the explicit return functions before your function exited, but most of them will lead to a timeout or exceeding memory limit kind of error.

The chances of you writing code that falls in a niche not covered by other errors are quite good. This can happen if you call upstream services, like DynamoDB, for example, and they fail silently for some reason.

How to Solve This Error?

Using a standalone Lambda function doesn’t make much sense—usually, you use one to connect other services with some transformation and business logic.

Your best course of action here is to look into the limits of these services and all the places where you call them with the AWS SDK. If you sent too much data at once or forgot some attributes in the SDK call, things can fail without any explicit error message.

If you use the asynchronous function style to signal that your function has completed, you can’t forget to call the right functions at the end anymore.

Read our blog

4 Tips for AWS Lambda Performance Optimization

In this article, we’re covering 4 tips for AWS Lambda optimization for production. Covering error handling, memory provisioning, monitoring, performance, and more.

AWS Lambda Free Tier: Where Are The Limits?

In this article we’ll go through the ins and outs of AWS Lambda pricing model, how it works, what additional charges you might be looking at and what’s in the fine print.

AWS Lambda vs EC2: A Comparative Guide

More articles

Made by developers for developers

Dashbird was born out of our own need for an enhanced serverless debugging and monitoring tool, and we take pride in being developers.

What our customers say

Dashbird gives us a simple and easy to use tool to have peace of mind and know that all of our Serverless functions are running correctly. We are instantly aware now if there’s a problem. We love the fact that we have enough information in the Slack notification itself to take appropriate action immediately and know exactly where the issue occurred.

Thanks to Dashbird the time to discover the occurrence of an issue reduced from 2-4 hours to a matter of seconds or minutes. It also means that hundreds of dollars are saved every month.

Great onboarding: it takes just a couple of minutes to connect an AWS account to an organization in Dashbird. The UI is clean and gives a good overview of what is happening with the Lambdas and API Gateways in the account.

I mean, it is just extremely time-saving. It’s so efficient! I don’t think it’s an exaggeration or dramatic to say that Dashbird has been a lifesaver for us.

Dashbird provides an easier interface to monitor and debug problems with our Lambdas. Relevant logs are simple to find and view. Dashbird’s support has been good, and they take product suggestions with grace.

Great UI. Easy to navigate through CloudWatch logs. Simple setup.

Dashbird helped us refine the size of our Lambdas, resulting in significantly reduced costs. We have Dashbird alert us in seconds via email when any of our functions behaves abnormally. Their app immediately makes the cause and severity of errors obvious.