Skip to content

Sentry instrumentation.ts register() not invoked in serverless function bundle on Netlify-OpenNext + Next.js 16 #3503

Description

@fenrici1

Summary

In a Next.js 16 app deployed on Netlify via @netlify/plugin-nextjs, the Next.js instrumentation.ts hook never fires on serverless function cold-starts. As a result, Sentry.init() (called from inside register()) never runs, Sentry.getClient() returns undefined in API route handlers, and Sentry.captureException(...) calls return an event ID but Sentry.flush() returns false — events are dropped before transport.

The Sentry project receives events from other paths (Sentry CLI synthetic test, dashboard "Send Test Event"), so DSN config and network reachability are confirmed. Only in-app captures via the SDK fail.

Stack

Component Version
Next.js ^16.2.4
@sentry/nextjs ^10.51.0
@netlify/plugin-nextjs ^5.15.10
Runtime (per process.env.NEXT_RUNTIME) nodejs

Symptom

A diagnostic API route handler returned:

{
  "runtime": "nodejs",
  "nodeEnv": "production",
  "hasDSN": true,
  "dsnMasked": "o....ingest.us.sentry.io/projects/...",
  "sentryClientPresent": false,
  "sentryEnabled": false,
  "sentryDsnInClient": false,
  "captureResult": "eventId=<uuid> flushed=false"
}

The DSN env var is set on Netlify and accessible to the function (hasDSN: true), but Sentry.getClient() returns undefined → Sentry.init() was never executed in this Lambda's cold-start.

What I tried

  1. Static imports in instrumentation.ts (per the OpenNext common-issues recommendation):

    import * as Sentry from "@sentry/nextjs";
    import { initServerSentry } from "./sentry.server.config";
    import { initEdgeSentry } from "./sentry.edge.config";
    
    export async function register() {
      if (process.env.NEXT_RUNTIME === "nodejs") initServerSentry();
      if (process.env.NEXT_RUNTIME === "edge") initEdgeSentry();
    }
    
    export const onRequestError = Sentry.captureRequestError;

    sentry.server.config.ts and sentry.edge.config.ts were refactored to export named init functions instead of calling Sentry.init() at module top-level. Did not fix.

  2. outputFileTracingIncludes in next.config.ts to force-include the files in the Lambda bundle:

    outputFileTracingIncludes: {
      "/**": [
        "./instrumentation.ts",
        "./sentry.server.config.ts",
        "./sentry.edge.config.ts",
        "./sentry.scrub.ts",
      ],
    },

    Did not fix when applied alongside Add in next on netlify banner, some copy changes for the plugin #1.

  3. withSentryConfig wrapper with org, project, silent, widenClientFileUpload — present and correct; source maps upload successfully on deploy.

Workaround that works

Adding an explicit Sentry.init() at the top of the route file makes everything work:

import * as Sentry from "@sentry/nextjs";

if (!Sentry.getClient() && process.env.NEXT_PUBLIC_SENTRY_DSN) {
  Sentry.init({
    dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
    enabled: true,
  });
}

After this, sentryClientPresent: true, flushed: true, and events land in Sentry within seconds. This confirms the SDK, the DSN, and the network path are all functional — the only failure is instrumentation.ts's register() not being invoked.

Hypothesis

@netlify/plugin-nextjs (built on opennextjs-netlify) bundles each Next.js route as an independent Lambda. The cold-start of those Lambdas appears to load the route handler directly without invoking Next.js's instrumentation hook, even though Next.js 15+ documents instrumentation.ts as auto-invoked at server startup.

This may need either:

  • A change in the Netlify adapter to call register() on cold-start, or
  • Documentation guidance recommending a different init pattern for users on Netlify

Reference

OpenNext common-issues page calls out dropped dynamic imports in instrumentation.ts (which I addressed via the static-import refactor in #1 above). The static-import fix alone, however, was insufficient in this case — the file appears not to be evaluated at all on Lambda cold-start.

Happy to provide a minimal repro repo if useful.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions