dherault / serverless-offline

Emulate AWS λ and API Gateway locally when developing your Serverless project
MIT License
5.19k stars 795 forks source link

UnhandledPromiseRejectionWarning - createLambdaContext #661

Closed iamskar closed 5 years ago

iamskar commented 5 years ago

Hi,

I have a simple code and i am running into this error :

(node:64802) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'done' of undefined at createLambdaContext (/Users/suri/Documents/Projects/Aptimise/subscription-services/node_modules/serverless-offline/src/index.js:671:44) at fail (/Users/suri/Documents/Projects/Aptimise/subscription-services/node_modules/serverless-offline/src/createLambdaContext.js:18:21) at process._tickCallback (internal/process/next_tick.js:68:7) (node:64802) 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: 2)

my code

`"use strict";

const fetch = require("node-fetch");

module.exports.handler = async (event, context, callback) => { try { const response = await fetch("https://reqres.in/api/users").catch(err => callback(err) ); const data = await response.json(); console.log(data); // if (!response.ok) { // // NOT res.status >= 200 && res.status < 300 // return { statusCode: response.status, body: response.statusText }; // } //const data = await response.json();

// callback(null, JSON.stringify({ msg: data }))

} catch (err) { //callback(err); } //console.log(data); callback(null, { statusCode: 200, body: JSON.stringify({ data: "somedata" }) }); }; `

Packages -

"serverless-offline": "^4.10.2" "node-fetch": "^2.5.0", "serverless: "1.42.3"

What am i doing wrong here ?

nfantone commented 5 years ago

Same over here.

(node:14604) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'done' of undefined
    at createLambdaContext (/project/node_modules/serverless-offline/src/index.js:671:44)
    at fail (/project/node_modules/serverless-offline/src/createLambdaContext.js:18:21)
    at process._tickCallback (internal/process/next_tick.js:68:7)
(node:14604) 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)

Seems like this line -> if (this.requests[requestId].done) { in serverless-offline/src/index.js:671 raises the exception.

EDIT Added the following above the faulty line in there and seemed to work for me.

this.requests[requestId] = this.requests[requestId] || {};
stgogm commented 5 years ago

I can also confirm this. It was introduced on version 4.10.1. Version 4.10.0 works OK.

Function:

module.exports.handler = async event => {
  try {
    // ...

    await someDatabaseQuery(); // Fulfills OK

    // ...

    return {
      // Success response...
    };
  } catch (err) {
    return {
      // Error response...
    };
  }
};

Output:

(node:8187) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'done' of undefined
    at createLambdaContext (/project/node_modules/serverless-offline/src/index.js:662:44)
    at fail (/project/node_modules/serverless-offline/src/createLambdaContext.js:18:21)
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:229:7)
(node:8187) 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)
dnalborczyk commented 5 years ago

duplicate of https://github.com/dherault/serverless-offline/issues/659

dherault commented 5 years ago

Please check out the v4.10.3 and tell us if it is ok now :)

starryalley commented 5 years ago

I can confirm on my system v4.10.3 works fine :) Thanks.

iamskar commented 5 years ago

Confirmed works fine with "serverless-offline": "^4.10.3" 👍 Thanks @dherault

dherault commented 5 years ago

Thank @Andorbal 👍

fshidemo commented 5 years ago

Hi i'm having same issue, when run with NestJs here. Even if with "^4.10.3". https://github.com/fshidemo/serverless-nestjs

I think: this.requests[requestId] = this.requests[requestId] || {};

or something like this still need to control this situation

if (requestId in this.requests) { // do some logic } else { // do else }

mkondel commented 5 years ago

The same error still persists with ^4.10.3.

nfantone commented 5 years ago

It seems to be working fine for me in latest 4.10.3.

mkondel commented 5 years ago

Can be verified that it's not working with 4.10.3 in this demo: https://github.com/mkondel/serverless-offline-test

aaronjensen commented 5 years ago

It's still failing for me in 4.10.3 as well.

(node:2229) UnhandledPromiseRejectionWarning: TypeError: Cannot read property 'done' of undefined
    at createLambdaContext (/home/circleci/project/node_modules/serverless-offline/src/index.js:671:44)
    at handler.catch.then.then.response (/home/circleci/project/functions/dist/service/src/handlers/webpack:/packages/service/src/lib/apiGatewayHandler.ts:133:1)
    at <anonymous>
    at process._tickDomainCallback (internal/process/next_tick.js:229:7)
Andorbal commented 5 years ago

My fix addressed the truly async promise scenario, but it looks like @mkondel issue is a truly async callback scenario. I've got a failing test that proves this out:

    it('should support handler that defers and uses done()', done => {
      const offline = new OfflineBuilder(new ServerlessBuilder(serverless))
        .addFunctionHTTP('index', {
          path: 'index',
          method: 'GET',
        }, (request, context, serverlessDone) => 
          setTimeout(() => 
            serverlessDone({
              statusCode: 200,
              body: JSON.stringify({ message: 'Hello World' }),
            }), 
          10)
        ).toObject();

      offline.inject({
        method: 'GET',
        url: '/index',
        payload: { data: 'input' },
      }, res => {
        expect(res.headers).to.have.property('content-type').which.contains('application/json');
        expect(res.statusCode).to.eq(200);
        expect(res.payload).to.eq('{"message":"Hello World"}');
        done();
      });
    });

Put that right after the test "should support handler returning Promise that defers" in offline.js and when you run tests, you'll see it fails with the error everyone is seeing. I don't know enough about that memory leak fix to know why the finally block is the way it is, so I'm not sure what to do about this specific scenario. I can personally ignore it because I'm using promises all the way down.

@mkondel, you can work around your problem by replacing line 5 of lambdas/index.js with:

module.exports.react = (event, context) => sls(server, { binary: binaryMimeTypes })(event, context);

It's not pretty, but it looks like serverless-http is getting the done() function from serverless-offline and assuming that you want to use a callback and not a promise. serverless-offline is giving you the option to use either, and figures out which you wanted to use by whether you get a promise back or not. So that puts you in the currently broken scenario.

mkondel commented 5 years ago

@Andorbal thank you for that work around, I will test it out.

Edit: Your fix now works in the demo and in my full app!

dherault commented 5 years ago

@Andorbal Can you PR a fix for this bug this week ? Otherwise I'll revert to 4.10.0

dherault commented 5 years ago

What about v4.10.4?

aaronjensen commented 5 years ago

@dherault seems to work for me.