Repository: getsentry/sentry-javascript (2025-07-15T10:52:50Z)

Issue:
NextJs 15.3 shows warning after adding Sentry
### Is there an existing issue for this?

- [x] I have checked for existing issues https://github.com/getsentry/sentry-javascript/issues
- [x] I have reviewed the documentation https://docs.sentry.io/
- [x] I am using the latest SDK release https://github.com/getsentry/sentry-javascript/releases

### How do you use Sentry?

Sentry Saas (sentry.io)

### Which SDK are you using?

@sentry/nextjs

### SDK Version

9.13

### Framework Version

15.3

### Link to Sentry event

_No response_

### Reproduction Example/SDK Setup

_No response_

### Steps to Reproduce

1. Install NextJs 15.3
2. Add Sentry using the wizard
3. Run `npm run dev`
4. See the following warning

### Expected Result

No warning or a better way to understand that this warning comes in combination with Sentry. Since this is a NextJs warning I started to look into the turbopack configuration..

### Actual Result

```diff
next dev --turbopack

   ▲ Next.js 15.3.0 (Turbopack)
   - Local:        http://localhost:3000
   - Network:      http://192.168.215.4:3000
   - Experiments (use with caution):
     · clientTraceMetadata

 ✓ Starting...
 ✓ Compiled instrumentation Node.js in 379ms
 ✓ Compiled instrumentation Edge in 134ms
 ✓ Ready in 1210ms
++ ⚠ Webpack is configured while Turbopack is not, which may cause problems.
++ ⚠ See instructions if you need to configure Turbopack:
  https://nextjs.org/docs/app/api-reference/next-config-js/turbo
```


Discussion:
This is known but you can safely ignore it. Next.js emits this warning because the Sentry SDK defines a webpack callback field and when you run with turbopack Next.js warns you in case it is critical for your app to run the webpack callback.
We can potentially fix this in the future when we only support Next.js versions that set the `process.env.TURBOPACK` env var when turbopack is used. Right now we need to unconditionally add the webpack callback because we cannot reliably detect turbopack due to old versions.

Maybe we can also add some flag to the SDK that will skip over adding this field.
+1 on this. Your solution sounds good @lforst 
@lforst Any updates on this?
Any update on this?
Not yet. I'll assign this and see what we can do
+1
## A PR closing this issue has just been released 🚀

This issue was referenced by PR #17013, which was included in the [9.39.0 release](https://github.com/getsentry/sentry-javascript/releases/tag/9.39.0).
🕺



Patch:
diff --git a/packages/nextjs/src/config/types.ts b/packages/nextjs/src/config/types.ts
index 81ee686ce205..b29fbb6881af 100644
--- a/packages/nextjs/src/config/types.ts
+++ b/packages/nextjs/src/config/types.ts
@@ -486,6 +486,18 @@ export type SentryBuildOptions = {
    */
   disableManifestInjection?: boolean;
 
+  /**
+   * Disables automatic injection of Sentry's Webpack configuration.
+   *
+   * By default, the Sentry Next.js SDK injects its own Webpack configuration to enable features such as
+   * source map upload and automatic instrumentation. Set this option to `true` if you want to prevent
+   * the SDK from modifying your Webpack config (for example, if you want to handle Sentry integration manually
+   * or if you are on an older version of Next.js while using Turbopack).
+   *
+   * @default false
+   */
+  disableSentryWebpackConfig?: boolean;
+
   /**
    * Contains a set of experimental flags that might change in future releases. These flags enable
    * features that are still in development and may be modified, renamed, or removed without notice.
diff --git a/packages/nextjs/src/config/withSentryConfig.ts b/packages/nextjs/src/config/withSentryConfig.ts
index 5ba768f3d0b7..85ade8e682de 100644
--- a/packages/nextjs/src/config/withSentryConfig.ts
+++ b/packages/nextjs/src/config/withSentryConfig.ts
@@ -311,9 +311,10 @@ function getFinalConfigObject(
             ],
           },
         }),
-    webpack: !isTurbopack
-      ? constructWebpackConfigFunction(incomingUserNextConfigObject, userSentryOptions, releaseName, routeManifest)
-      : undefined,
+    webpack:
+      isTurbopack || userSentryOptions.disableSentryWebpackConfig
+        ? incomingUserNextConfigObject.webpack // just return the original webpack config
+        : constructWebpackConfigFunction(incomingUserNextConfigObject, userSentryOptions, releaseName, routeManifest),
     ...(isTurbopackSupported && isTurbopack
       ? {
           turbopack: constructTurbopackConfig({


Test patch:
diff --git a/packages/nextjs/test/config/withSentryConfig.test.ts b/packages/nextjs/test/config/withSentryConfig.test.ts
index f1f1ae13f9fa..3b872d810c49 100644
--- a/packages/nextjs/test/config/withSentryConfig.test.ts
+++ b/packages/nextjs/test/config/withSentryConfig.test.ts
@@ -145,6 +145,130 @@ describe('withSentryConfig', () => {
     });
   });
 
+  describe('webpack configuration behavior', () => {
+    const originalTurbopack = process.env.TURBOPACK;
+
+    afterEach(() => {
+      vi.restoreAllMocks();
+      process.env.TURBOPACK = originalTurbopack;
+    });
+
+    it('uses constructed webpack function when Turbopack is disabled and disableSentryWebpackConfig is false/undefined', () => {
+      delete process.env.TURBOPACK;
+
+      // default behavior
+      const finalConfigUndefined = materializeFinalNextConfig(exportedNextConfig);
+      expect(finalConfigUndefined.webpack).toBeInstanceOf(Function);
+
+      const sentryOptions = {
+        disableSentryWebpackConfig: false,
+      };
+      const finalConfigFalse = materializeFinalNextConfig(exportedNextConfig, undefined, sentryOptions);
+      expect(finalConfigFalse.webpack).toBeInstanceOf(Function);
+    });
+
+    it('preserves original webpack config when disableSentryWebpackConfig is true (regardless of Turbopack)', () => {
+      const originalWebpackFunction = vi.fn();
+      const configWithWebpack = {
+        ...exportedNextConfig,
+        webpack: originalWebpackFunction,
+      };
+
+      const sentryOptions = {
+        disableSentryWebpackConfig: true,
+      };
+
+      delete process.env.TURBOPACK;
+      const finalConfigWithoutTurbopack = materializeFinalNextConfig(configWithWebpack, undefined, sentryOptions);
+      expect(finalConfigWithoutTurbopack.webpack).toBe(originalWebpackFunction);
+
+      process.env.TURBOPACK = '1';
+      vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.3.0');
+      const finalConfigWithTurbopack = materializeFinalNextConfig(configWithWebpack, undefined, sentryOptions);
+      expect(finalConfigWithTurbopack.webpack).toBe(originalWebpackFunction);
+    });
+
+    it('preserves original webpack config when Turbopack is enabled (ignores disableSentryWebpackConfig flag)', () => {
+      process.env.TURBOPACK = '1';
+      vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.3.0');
+
+      const originalWebpackFunction = vi.fn();
+      const configWithWebpack = {
+        ...exportedNextConfig,
+        webpack: originalWebpackFunction,
+      };
+
+      const sentryOptionsWithFalse = {
+        disableSentryWebpackConfig: false,
+      };
+      const finalConfigWithFalse = materializeFinalNextConfig(configWithWebpack, undefined, sentryOptionsWithFalse);
+      expect(finalConfigWithFalse.webpack).toBe(originalWebpackFunction);
+
+      const finalConfigWithUndefined = materializeFinalNextConfig(configWithWebpack);
+      expect(finalConfigWithUndefined.webpack).toBe(originalWebpackFunction);
+
+      const sentryOptionsWithTrue = {
+        disableSentryWebpackConfig: true,
+      };
+      const finalConfigWithTrue = materializeFinalNextConfig(configWithWebpack, undefined, sentryOptionsWithTrue);
+      expect(finalConfigWithTrue.webpack).toBe(originalWebpackFunction);
+    });
+
+    it('preserves original webpack config when Turbopack is enabled and disableSentryWebpackConfig is true', () => {
+      process.env.TURBOPACK = '1';
+      vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.3.0');
+
+      const sentryOptions = {
+        disableSentryWebpackConfig: true,
+      };
+
+      const originalWebpackFunction = vi.fn();
+      const configWithWebpack = {
+        ...exportedNextConfig,
+        webpack: originalWebpackFunction,
+      };
+
+      const finalConfig = materializeFinalNextConfig(configWithWebpack, undefined, sentryOptions);
+
+      expect(finalConfig.webpack).toBe(originalWebpackFunction);
+    });
+
+    it('preserves undefined webpack when Turbopack is enabled, disableSentryWebpackConfig is true, and no original webpack config exists', () => {
+      process.env.TURBOPACK = '1';
+      vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.3.0');
+
+      const sentryOptions = {
+        disableSentryWebpackConfig: true,
+      };
+
+      const configWithoutWebpack = {
+        ...exportedNextConfig,
+      };
+      delete configWithoutWebpack.webpack;
+
+      const finalConfig = materializeFinalNextConfig(configWithoutWebpack, undefined, sentryOptions);
+
+      expect(finalConfig.webpack).toBeUndefined();
+    });
+
+    it('includes turbopack config when Turbopack is supported and enabled', () => {
+      process.env.TURBOPACK = '1';
+      vi.spyOn(util, 'getNextjsVersion').mockReturnValue('15.3.0');
+
+      const finalConfig = materializeFinalNextConfig(exportedNextConfig);
+
+      expect(finalConfig.turbopack).toBeDefined();
+    });
+
+    it('does not include turbopack config when Turbopack is not enabled', () => {
+      delete process.env.TURBOPACK;
+
+      const finalConfig = materializeFinalNextConfig(exportedNextConfig);
+
+      expect(finalConfig.turbopack).toBeUndefined();
+    });
+  });
+
   describe('release injection behavior', () => {
     afterEach(() => {
       vi.restoreAllMocks();