Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

UnhandledRejection getting caught as UncaughtException #6963

Closed
3 tasks done
Sammaye opened this issue Jan 28, 2023 · 11 comments
Closed
3 tasks done

UnhandledRejection getting caught as UncaughtException #6963

Sammaye opened this issue Jan 28, 2023 · 11 comments
Assignees
Labels
Package: node Issues related to the Sentry Node SDK Type: Bug

Comments

@Sammaye
Copy link

Sammaye commented Jan 28, 2023

Is there an existing issue for this?

How do you use Sentry?

Sentry Saas (sentry.io)

Which package are you using?

@sentry/node

SDK Version

7.34.0

Framework Version

4.17.1

Link to Sentry event

https://delenta.sentry.io/issues/3902577770/?project=6739047&query=is%3Aunresolved&referrer=issue-stream

SDK Setup

        global.process.on('uncaughtException', (error) => {
            console.log('in uncaught exception', error);
        });

        const sentryOptions = {
            environment: config.APP_ENV === 'DEV'
                ? 'development'
                : (
                    config.APP_ENV.includes('ALPHA')
                        ? 'alpha'
                        : (config.APP_ENV === 'BETA' ? 'beta' : 'production')
                ),
            dsn: config.LOGS_SENTRY_DSN,
            // Set tracesSampleRate to 1.0 to capture 100%
            // of transactions for performance monitoring.
            // We recommend adjusting this value in production
            tracesSampleRate: 0.25,
            integrations: integrations => {
                const newIntegrations = integrations.filter(integration =>
                    integration.name !== 'OnUncaughtException' &&
                    integration.name !== 'Http' &&
                    integration.name !== 'LocalVariables'
                );

                newIntegrations.push(
                    new Sentry.Integrations.OnUncaughtException({exitEvenIfOtherHandlersAreRegistered: false}),
                    new Sentry.Integrations.Http({tracing: true}),
                    new Sentry.Integrations.LocalVariables({
                        captureAllExceptions: true,
                    })
                );

                if (app) {
                    newIntegrations.push(
                        // enable Express.js middleware tracing
                        new Tracing.Integrations.Express({app}),
                    );
                }

                return newIntegrations;
            },
        };

        Sentry.init(sentryOptions);

Steps to Reproduce

Make a new express action like:

export async function a(req, res, next) {
    try {
        const [
            ab,
        ] = await Promise.all([
            b(),
        ]);

        return res.status(200).json({
              ab
        });
    }catch(error){
        console.log('caught error');
        next(error);
    }
}

async b() {
    throw new Error('omg');
    return this.countsdtdfdst({});
}

And then simply attach this action to any route, like /blah and go to this route in something like postman

Expected Result

It handles this via the express middleware correctly and assigns all express information.

Actual Result

It instead strips all that and throws it as a uncaught exception:

[ERROR] 11:04:08 Error: omg
in uncaught exception Error: omg
    at Function.b
caught error
GET /blah 503 437.030 ms - 87
(node:3408) PromiseRejectionHandledWarning: Promise rejection was handled asynchronously (rejection id: 1)

If I remove the new Sentry.Integrations.OnUncaughtException({exitEvenIfOtherHandlersAreRegistered: false}), line it works

@Sammaye
Copy link
Author

Sammaye commented Jan 28, 2023

As a note: the example above is a bit contrived since I have found out that the unhandledRejection is being thrown by mongoose in a later part of the Promise.all(), however, the issue still stands, when I remove sentry unhandledRejections are correctly handled, but with Sentry unhandledReejections redirect to uncaughtExceptions.

@Sammaye
Copy link
Author

Sammaye commented Jan 28, 2023

As another note, this line:

app.use(Sentry.Handlers.requestHandler());

seems to cause the redirect, if I remove it the error is treated as a unhandled rejection

@Lms24
Copy link
Member

Lms24 commented Jan 30, 2023

Hey @Sammaye thanks for writing in! This seems to be very much related to #6750.

If I remove the new Sentry.Integrations.OnUncaughtException({exitEvenIfOtherHandlersAreRegistered: false}), line it works.

That's interesting, cause afaik this option should exactly prevent exiting. So what you could do instead is just register an uncaught exception handler yourself to have this effect (or remove the default integration). I know that this doesn't really fix the error but it might be a worthwhile workaround.

Let me reproduce this error to see if we can actually do something against this without a breaking change. This seems to be quite a complicated topic as described in #6750 and #5627. I can't guarantee a proper fix here, sorry :(

@Lms24 Lms24 self-assigned this Jan 30, 2023
@Lms24 Lms24 added Status: Needs Reproduction Package: node Issues related to the Sentry Node SDK and removed Status: Untriaged labels Jan 30, 2023
@Lms24
Copy link
Member

Lms24 commented Jan 30, 2023

I've tried to reproduce this locally but didn't get the app to crash yet. Basically I took your example and just cut it down a little. Would you mind taking a look at this code and tell me if I missed something?

const Sentry = require("@sentry/node");

const express = require("express");

global.process.on("uncaughtException", (error) => {
  console.log("in uncaught exception", error);
});

const app = express();

Sentry.init({
  environment: "production",
  dsn: "__DSN__",
  integrations: (integrations) => {
    const newIntegrations = integrations.filter(
      (integration) =>
        integration.name !== "OnUncaughtException" &&
        integration.name !== "LocalVariables"
    );

    newIntegrations.push(
      new Sentry.Integrations.OnUncaughtException({
        exitEvenIfOtherHandlersAreRegistered: false,
      }),
      new Sentry.Integrations.LocalVariables({
        captureAllExceptions: true,
      })
    );

    return newIntegrations;
  },
});

app.use(Sentry.Handlers.requestHandler());

async function a(req, res, next) {
  try {
    const [ab] = await Promise.all([b()]);

    return res.status(200).json({
      ab,
    });
  } catch (error) {
    console.log("caught error");
    next(error);
  }
}

async function b() {
  throw new Error("omg");
  return this.countsdtdfdst({});
}

app.get("/test", a);

app.use(Sentry.Handlers.errorHandler());

app.listen(3000, () => {
  console.log("server started on port 3000");
});

Calling GET localhost:3000/test leads to the following console output:

server started on port 3000
caught error
Error: omg

Plus, the error is sent to my sentry project

@Sammaye
Copy link
Author

Sammaye commented Jan 30, 2023

The issue is that as I found out, that doesn't cause an unhandledrejection, I thought it did but as comment I discovered that it was actually the use of mongoose within the Promise.all() that caused the issue, very weird one, but anyway yeah would have to artificially make an unhadledrejection

@Sammaye
Copy link
Author

Sammaye commented Jan 30, 2023

Also this could be quite hard to replicate since I think it was an issue in nodejs as to why this was event happening, let me expand:

async function a(req, res, next) {
  try {
    const [ab, bc] = await Promise.all([b(), (await c()).length]);

    return res.status(200).json({
      ab,
      bc
    });
  } catch (error) {
    console.log("caught error");
    next(error);
  }
}

async c () {
    // this is a  mongoose reference
    this.mdel.find({o:1})
}

I am unsure if it ha-pens to unhandledrejections in general since it seems to be a express middleware causing the redirect and by then everything should be caught in theory. Still not entirely sure what nodejs was doing but when it rejected b() it also rejecxt c() async so it caused an unhandledrejction

@Lms24
Copy link
Member

Lms24 commented Jan 31, 2023

I'm sorry but I still can't replicate the problem In fact I'm not sure if this is a problem of the Sentry SDK at all or if it is rather a Node/express problem when using async handlers.

Please provide a full reproduction example (e.g. a github repo or a full code snippet) that shows us how to reliably reproduce this. Also, what is your exact expectation that the SDK should handle differently?

@Sammaye
Copy link
Author

Sammaye commented Jan 31, 2023

I'll try and get one.

I do believe this is sentry since if I use winston then winston will not treat an unhandledRejection as a uncaughtException, like I said, when I remove sentry the error is treated as it should be by my legacy system which is just a basic winston logger.

Though considering how rare it is that someone will encounter this, since I am not sure if this is a bug nodejs (due to that fact that it is throwing duplicate errors, one caught and one uncaught) it might not be worth the effort.

@github-actions
Copy link
Contributor

This issue has gone three weeks without activity. In another week, I will close it.

But! If you comment or otherwise update it, I will reset the clock, and if you label it Status: Backlog or Status: In Progress, I will leave it alone ... forever!


"A weed is but an unloved flower." ― Ella Wheeler Wilcox 🥀

@github-actions github-actions bot closed this as not planned Won't fix, can't repro, duplicate, stale Mar 1, 2023
@cjwiseman11
Copy link

We are seeing this issue where unhandledRejection's are now being thrown as uncaughtException's. It seems this is happening when upgrading from Node 14 to Node 18. (On Sentry 7.20.1)

We have a global unhandledRejection handler which dealt with unhandled rejections so even with the flag change in Node 18 to "unhandled-rejections=strict", we shouldn't see server crashes due to our global handler.

Now they are being thrown as uncaughtException, it completely crashes the app. Everything works as expected if I remove

app.use(sentry.Handlers.requestHandler());

Is there any workaround for this?

@extradosages
Copy link

extradosages commented Jul 12, 2023

@cjwiseman11 If you haven't already resolved this, upgrading @sentry/node to the latest version (^7.58.0 right now) seems to have resolved the issue for us.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Package: node Issues related to the Sentry Node SDK Type: Bug
Projects
None yet
Development

No branches or pull requests

4 participants