honeybadger-js: Unable to get an error thrown from inside the handler after wrapping it in lambdaHandler

What are the steps to reproduce this issue?

  1. Create an APIGateway handler method for AWS lambda
  2. Throw an error inside the handler.
  3. Add a test to execute the handler to throw an error. The error never gets thrown.

I have my main handler as defined below:

import { APIGatewayProxyHandler } from 'aws-lambda';
import Honeybadger from '@honeybadger-io/js';
const handler: APIGatewayProxyHandler = async(event, context) => {
	//performs some logic or throw an error
	try { // blah } 
	catch(err) { throw new Error('Some Error); }
}

export default Honeybadger.lambdaHandler(handler);

In my tests I import this handler and want the test to fail

import handler form '../index.ts';
describe('Handler', () => {
  test('Error is handled', async () => {
      try { 
        // valid event and context are manually added fixtures
         await handler(validEvent, context);
         throw new Error('handler should throw an error');
      } catch(err) {
         expect((<Error>e).message).toBe('Some Error');
      }
  });
});

What happens?

There are two things that are happening wrong.

  • The handler code doesn’t get executed synchronously during the tests. Any console.log that I add to the handler show up after the test has ended. I am using Jest for testing.
  • The handler never throws the error as is being expected due to it being executed in an async manner.

What were you expecting to happen?

I was expecting that my handlers and tests keep on working as they were before I started wrapping them in lambdaHandler.

Any logs, error output, etc?

As stated above, the code works in an async manner. I see logs after the test has ended. The await doesn’t seem to work.

Any other comments?

It looks like the wrapper is not using Promise. It expects a callback to be passed to it to which is passes the error in case and error is produced. Please correct me if my understanding is wrong.

What versions are you using?

Operating System: Mac OS Big Sur 11.4 (20F71)Package Version: "@honeybadger-io/js": "^3.2.5"Browser Version: Node 14, Typescript, “aws-lambda”: “^1.0.6”

About this issue

  • Original URL
  • State: closed
  • Created 3 years ago
  • Comments: 20 (11 by maintainers)

Commits related to this issue

Most upvoted comments

I’m just glad I was able to help! Please don’t hesitate to contact us again if you have any questions/suggestions/issues in the future!

Awesome, you will not regret it! And yes, we will release an update asap.

As an additional question. Is there a plan to move this to a Promise based mechanism to that we can simply use async/await when calling the lambdaHandler instead of passing in a callback deliberately?

There is some discussion on refactoring our code to support promises, but I would direct this question to @joshuap . However, you could wrap the handler to a promise and call async/await yourself until we implement this officially:

function handlerPromise (event, context) {
  return new Promise<void>((resolve, reject) => {
    handler(event, context, (err) => {
      if (err) return reject(err)
      resolve()
    })
  })
}
...
// then call this with await
await handlerPromise({}, {})

Thanks @subzero10. I think we can start integration of honeybadger on our end. I am assuming this would be released as a patch update and the package will get updated next time we to a fresh npm i.

As an additional question. Is there a plan to move this to a Promise based mechanism to that we can simply use async/await when calling the lambdaHandler instead of passing in a callback deliberately?

I understand. reportData is actually one of the preconditions I mentioned above. That’s why it behaves the same as not supplying an apiKey.

You are correct, my PR should call the callback even when reportData is false.

Hey @tweaktastic, After inspecting more thoroughly the code, I noticed that we actually support async lambda handlers! 😌 The code is not super readable because it has to support both async and non-async and that’s why I missed it on my first read. Honeybadger.lambdaHandler takes an async (or a callback-based handler) and returns a callback-based handler. Your tests are failing because you are calling Honybadger’s lambda handler with async/await. You can try calling it with a callback-based approach:

import handler from './index';
describe('Handler', () => {
    test('Error is handled', (done) => {
        handler({}, {}, (err?: Error, resp?: any) => {
            if (!err) {
                done(new Error('should not reach here'))
                return
            }
            expect(err.message).toBe('Some Error')
            done()
        });
    });
});

Let me know if that works for you so I can close the ticket. Thanks!

cc @joshuap Please ignore my comment above on creating a ticket.

Hey @tweaktastic, I’m glad to see that you are finally getting errors reported on Honeybadger.

We were earlier using notify to report errors. This doesn’t work with Lambdas as they exit even before the lib has made a call to honeybadger. With this we were getting only fraction of errors getting reported to us and thus started exploring lambdaHandler. Now the issue is, with notify we could add context to be reported to the honeybadger console. This enabled us to add custom trace ids and other details we needed to debug issues. But it looks like when we use lambdaHandlers nothing of that sort can get reported. Is there a way to add custom fields to be reported using lambdaHandler?

Have you looked into the beforeNotify handler? That could help achieve what you want. I’m not 100% sure though, since I haven’t read your source code. But it may be possible if you register some callbacks to be called in the beforeNotify handler. The callback is called with a Notice object with a context property that you can use to add custom data.

Secondly, I think this is the way lambdaHandler was deigned and it didn’t occur to me before. The only time an error gets reported to honeybadger is when an error is thrown from inside the main handler. However, we have some use cases where we return error to the caller (400, 403, 401, 429). We don’t raise an error but rather send a response back. In this case we would like to report an alarm but it doesn’t seem possible with lambdaHandler.

We may be able to add an option for that to happen automatically or see if we can come up with a workaround. Can you clarify what you mean with the phrase report an alarm? Personally, I think error codes similar to 400, 401, 403 and 429 should not be reported to your error monitoring tool but to your logging service (i.e. Cloudwatch), but I will look into that nevertheless.

Awaiting the above method does ensure that error gets reported but it does come with an increased execution times as it actually waits for http call to get completed.

Can you please confirm that the increased execution times refer to the time for the client to receive the response and not the lambda function execution time? Because it would make sense for the lambda function to have increased execution time (since it has to wait for the http request to complete), but ideally it should happen after the response is sent to the client.

@tweaktastic I’m glad that it works on your side also.

The patch should be pretty soon, but I think @joshuap is better suited to answer that.

@tweaktastic I’m really sorry you are facing issues with our package! Can you please try the fix in the branch of this PR?

Thank you for not giving up!

cc @joshuap

Hi @subzero10. Sorry to bother you again. But the callbacks are not working in the manner they should.

In one of our handlers we want to return a some data (statusCode, body). It looks like when wrap our handlers in lambdaHandler, the handlers keep on executing forever and never exit, thus never returning any response. You can check the update code in the repo. https://github.com/tweaktastic/honeybadger-testing The tests in which an error is thrown passes correctly, while the one in which we are waiting for a response from the handler timesout.

On checking this handler with serverless framework on my dev env I was able to see that the request keeps in execution without ever getting a response back.

Thanks for all your help @subzero10. I really appreciate your feedback and your quick response regarding this. We will wrap the handler to return the Promise as you have mentioned. Looking forward to use Honeybadger alerting.