getsentry / sentry-javascript

Official Sentry SDKs for JavaScript
https://sentry.io
MIT License
7.97k stars 1.57k forks source link

nextjs (12) withSentry API route wrapper crashes Nodejs with ERR_INTERNAL_ASSERTION #4164

Closed killercup closed 2 years ago

killercup commented 2 years ago

Package + Version

Version:

6.15.0

Description

I tried adding sentry support to an internal nextjs 12 project. I know version 12 is not supported, so I didn't expect everything to work out of the box.

I am exposing a Apollo Server instance as an API route, using apollo-server-micro like this:

import { ApolloServer } from "apollo-server-micro";
export const apolloServer = new ApolloServer(…);
const server = apolloServer.start().then(() =>
  apolloServer.createHandler({
    path: "/api/graphql",
  }),
);

async function apolloRequestHandler(req: IncomingMessage, res: ServerResponse) {
  const handler = await server;
  await handler(req, res);
}

To collect errors with sentry I expose this API route like this:

import { withSentry } from "@sentry/nextjs";

export default withSentry(apolloRequestHandler);

In development mode, everything works okay.

yarn && yarn build && yarn start starts the server just fine in production mode, but after one API request the Node process crashes with:

Error [ERR_INTERNAL_ASSERTION]: This is caused by either a bug in Node.js or incorrect usage of Node.js internals.
Please open an issue with this stack trace at https://github.com/nodejs/node/issues

    at new NodeError (internal/errors.js:322:7)
    at assert (internal/assert.js:14:11)
    at ServerResponse.detachSocket (_http_server.js:234:3)
    at resOnFinish (_http_server.js:795:7)
    at ServerResponse.emit (events.js:400:28)
    at ServerResponse.emit (domain.js:537:15)
    at onFinish (_http_outgoing.js:792:10)
    at afterWrite (internal/streams/writable.js:466:5)
    at afterWriteTick (internal/streams/writable.js:453:10)
    at processTicksAndRejections (internal/process/task_queues.js:81:21)
error Command failed with exit code 1.
info Visit https://yarnpkg.com/en/docs/cli/run for documentation about this command.

As this is not a critical project and I don't have time to dig into this further, I will only use sentry client side for now.

Let me know if you need more information.

Additional info

killercup commented 2 years ago

For whoever might find this, I have also added an explicit plugin for error collection to my Apollo server, which works just fine:

// from https://blog.sentry.io/2020/07/22/handling-graphql-errors-using-sentry
// (c) 2020 Bruno Scheufler

import * as Sentry from "@sentry/node";
import { ApolloError, PluginDefinition } from "apollo-server-core";

export const ApolloServerPluginLogToSentry: PluginDefinition = {
  async requestDidStart(_) {
    /* Within this returned object, define functions that respond
         to request-specific lifecycle events. */
    return {
      async didEncounterErrors(ctx: any) {
        // If we couldn't parse the operation, don't
        // do anything here
        if (!ctx.operation) {
          return;
        }

        for (const err of ctx.errors) {
          // Only report internal server errors,
          // all errors extending ApolloError should be user-facing
          if (err instanceof ApolloError) {
            continue;
          }

          // Add scoped report details and send to Sentry
          Sentry.withScope((scope) => {
            // Annotate whether failing operation was query/mutation/subscription
            scope.setTag("kind", ctx.operation.operation);

            // Log query and variables as extras (make sure to strip out sensitive data!)
            scope.setExtra("query", ctx.request.query);
            scope.setExtra("variables", ctx.request.variables);

            if (err.path) {
              // We can also add the path as breadcrumb
              scope.addBreadcrumb({
                category: "query-path",
                message: err.path.join(" > "),
                level: Sentry.Severity.Debug,
              });
            }

            const transactionId = ctx.request.http.headers.get("x-transaction-id");
            if (transactionId) {
              scope.setTransaction(transactionId);
            }

            Sentry.captureException(err);
          });
        }
      },
    };
  },
};
jpulec commented 2 years ago

This also impacts NextJS 11, not just 12. Running NextJS 11 with 6.15 throws these too.

tmcw commented 2 years ago

👋 Might want to try with Sentry before 6.14.3 - a change in how Sentry tweaks response streams is what caused a very similar crash in my application. See #4151: if your application calls stream.end() multiple times, then this'll happen.

DavidChouinard commented 2 years ago

Running latest Next.js & Sentry and getting this as well

poeschko commented 2 years ago

Downgrading to Sentry 6.14.1 resolved this for me.

lobsterkatie commented 2 years ago

Closing this in favor of https://github.com/getsentry/sentry-javascript/issues/4151.