Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Unhandled promise rejection on AWS lambda with async / await

Recently AWS announced the availability of the nodejs8.10 runtime for their lambda functions (Node.js 8.10 runtime available). While this seemed great for the happy flow, I'm running into some problems with the unhappy flow, i.e. I'm getting 'UnhandledPromiseRejectionWarnings'.

I'm using the below code. The idea is that I provide a key to a non-existent object to test out the unhappy flow for that scenario. My goal is for the error to get logged and propagated out of the lambda since I have no sane way of handling it here (I have no way to retrieve a new key within this lambda function). I also would like to be able to use the error in the caller (e.g. another lambda function or step functions).

'use strict';

const AWS = require('aws-sdk');

exports.handler = async (event) => {
  let data;
  try {
    data = await getObject(event.key);
  } catch (err) {
    console.error('So this happened:', err);
    throw err;
  }

  return data;
}

const getObject = async (key) => {
  let params = {
    Bucket: process.env.BUCKET,
    Key: key
  };

  const s3 = new AWS.S3();

  let data;
  try {
    data = await s3.getObject(params).promise();
  } catch(err) {
    console.log('Error retrieving object');
    throw err;
  }

  console.log('Retrieved data');
  return data.Body.toString('utf8');
}

If I run this lambda function (with SAM local) I get the error back out of the lambda like I want, but I also get the following warning(s):

2018-04-18T07:54:16.217Z    6ecc84eb-46f6-1b08-23fb-46de7c5ba6eb    (node:1) UnhandledPromiseRejectionWarning: NoSuchKey: The specified key does not exist.
    at Request.extractError (/var/task/node_modules/aws-sdk/lib/services/s3.js:577:35)
    at Request.callListeners (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:105:20)
    at Request.emit (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:77:10)
    at Request.emit (/var/task/node_modules/aws-sdk/lib/request.js:683:14)
    at Request.transition (/var/task/node_modules/aws-sdk/lib/request.js:22:10)
    at AcceptorStateMachine.runTo (/var/task/node_modules/aws-sdk/lib/state_machine.js:14:12)
    at /var/task/node_modules/aws-sdk/lib/state_machine.js:26:10
    at Request.<anonymous> (/var/task/node_modules/aws-sdk/lib/request.js:38:9)
    at Request.<anonymous> (/var/task/node_modules/aws-sdk/lib/request.js:685:12)
    at Request.callListeners (/var/task/node_modules/aws-sdk/lib/sequential_executor.js:115:18)
2018-04-18T07:54:16.218Z    6ecc84eb-46f6-1b08-23fb-46de7c5ba6eb    (node:1) UnhandledPromiseRejectionWarning: Unhandled promise rejection. This error originated either by throwing inside of an async function without a catch block, or by rejecting a promise which was not handled with .catch(). (rejection id: 1)
2018-04-18T07:54:16.218Z    6ecc84eb-46f6-1b08-23fb-46de7c5ba6eb    (node:1) [DEP0018] DeprecationWarning: Unhandled promise rejections are deprecated. In the future, promise rejections that are not handled will terminate the Node.js process with a non-zero exit code.

I'm not sure how to handle this while still propagating the error out of the lambda function (which should be a valid scenario according to lambda function errors (node.js))

I've also tried running a similar (to me) scenario like the one below (to try and pinpoint and understand the error) but somehow here I do not get the warning and it is working as intended (the error is returned from the lambda function).

'use strict';

const AWS = require('aws-sdk');

exports.handler = async (event) => {
  let data;
  try {
    data = await directBoom();
  } catch (err) {
    console.error('So this happened:', err);
    throw err;
  }

  return data;
}

const directBoom = async () => {
  let data;
  try {
    data = await Promise.reject(new Error('boom!')); 
  } catch(err) {
    throw err;
  }

  return data;
}

What am I missing here and why are the two examples behaving differently? How do I get rid of the warning in the first example while still being able to propagate the error out of the lambda function? Any help would be appreciated.

like image 603
CodeVision Avatar asked Apr 18 '18 08:04

CodeVision


People also ask

What happens on unhandled promise rejection?

The unhandledrejection event is sent to the global scope of a script when a JavaScript Promise that has no rejection handler is rejected; typically, this is the window , but may also be a Worker .

Does Lambda retry on timeout?

There are three reasons why retry and timeout issues occur when invoking a Lambda function with an AWS SDK: A remote API is unreachable or takes too long to respond to an API call. The API call doesn't get a response within the socket timeout.

Is Lambda deprecated?

Deprecation (end of support) for a runtime occurs in two phases. Phase 1 - Lambda no longer applies security patches or other updates to the runtime. You can no longer create functions that use the runtime, but you can continue to update existing functions.

Is not a function AWS Lambda?

When trying to invoke any of your lambda functions if you are getting an error that says “lambda is not a function” the problem is likely that the handler is misspelled in the serverless. yml file OR you aren't exporting modules. exports. handler and are instead exporting something else.


2 Answers

Any time you throw or reject within a promise/async function and don't handle it with a catch, Node is going to return that warning.

The example on AWS doesn't throw the error in the catch block, it returns it:

let AWS = require('aws-sdk');
let lambda = new AWS.Lambda();
let data;

exports.handler = async (event) => {
    try {
        data = await lambda.getAccountSettings().promise();
    }
    catch (err) {
        console.log(err);
        return err;
    }
    return data;
};

In a large application with lots of asynchronous functions, having a single unhandled promise terminate the node process would be bad. That may not apply to a simple Lambda function where throwing an error that terminates the process is desired behavior. But if you don't want Node to warn you, return the error instead.

like image 137
Ryan Flaherty Avatar answered Sep 21 '22 07:09

Ryan Flaherty


I managed that by using the fail method from the context object that notifies Lambda function that the execution failed.

'use strict'

exports.handler = async function (event, context) {
  try {
    throw new Error('Something went wrong')
  } catch (err) {
    context.fail(err)
  }
}
like image 27
Kevin Rambaud Avatar answered Sep 19 '22 07:09

Kevin Rambaud