Skip to content

fix(nextjs): Inject Next.js version for dev symbolication #17379

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

Merged
merged 7 commits into from
Aug 12, 2025
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,7 @@ type OriginalStackFrameResponse = {

const globalWithInjectedValues = GLOBAL_OBJ as typeof GLOBAL_OBJ & {
_sentryBasePath?: string;
next?: {
version?: string;
};
_sentryNextJsVersion: string | undefined;
};

/**
Expand All @@ -39,9 +37,15 @@ export async function devErrorSymbolicationEventProcessor(event: Event, hint: Ev
try {
if (hint.originalException && hint.originalException instanceof Error && hint.originalException.stack) {
const frames = stackTraceParser.parse(hint.originalException.stack);
const nextJsVersion = globalWithInjectedValues._sentryNextJsVersion;

// If we for whatever reason don't have a Next.js version,
// we don't want to symbolicate as this previously lead to infinite loops
if (!nextJsVersion) {
return event;
}

const nextjsVersion = globalWithInjectedValues.next?.version || '0.0.0';
const parsedNextjsVersion = nextjsVersion ? parseSemver(nextjsVersion) : {};
const parsedNextjsVersion = parseSemver(nextJsVersion);

let resolvedFrames: ({
originalCodeFrame: string | null;
Expand Down
20 changes: 20 additions & 0 deletions packages/nextjs/src/config/turbopack/constructTurbopackConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,11 @@ import type { NextConfigObject, TurbopackOptions, TurbopackRuleConfigItemOrShort
export function constructTurbopackConfig({
userNextConfig,
routeManifest,
nextJsVersion,
}: {
userNextConfig: NextConfigObject;
routeManifest?: RouteManifest;
nextJsVersion?: string;
}): TurbopackOptions {
const newConfig: TurbopackOptions = {
...userNextConfig.turbopack,
Expand All @@ -40,6 +42,24 @@ export function constructTurbopackConfig({
});
}

if (nextJsVersion) {
newConfig.rules = safelyAddTurbopackRule(newConfig.rules, {
matcher: '**/instrumentation.*',
rule: {
loaders: [
{
loader: path.resolve(__dirname, '..', 'loaders', 'valueInjectionLoader.js'),
options: {
values: {
_sentryNextJsVersion: nextJsVersion,
},
},
},
],
},
});
}

return newConfig;
}

Expand Down
37 changes: 28 additions & 9 deletions packages/nextjs/src/config/webpack.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ export function constructWebpackConfigFunction(
userSentryOptions: SentryBuildOptions = {},
releaseName: string | undefined,
routeManifest: RouteManifest | undefined,
nextJsVersion: string | undefined,
): WebpackConfigFunction {
// Will be called by nextjs and passed its default webpack configuration and context data about the build (whether
// we're building server or client, whether we're in dev, what version of webpack we're using, etc). Note that
Expand Down Expand Up @@ -90,7 +91,15 @@ export function constructWebpackConfigFunction(
const newConfig = setUpModuleRules(rawNewConfig);

// Add a loader which will inject code that sets global values
addValueInjectionLoader(newConfig, userNextConfig, userSentryOptions, buildContext, releaseName, routeManifest);
addValueInjectionLoader({
newConfig,
userNextConfig,
userSentryOptions,
buildContext,
releaseName,
routeManifest,
nextJsVersion,
});

addOtelWarningIgnoreRule(newConfig);

Expand Down Expand Up @@ -682,14 +691,23 @@ function setUpModuleRules(newConfig: WebpackConfigObject): WebpackConfigObjectWi
*/
// TODO: Remove this loader and replace it with a nextConfig.env (https://web.archive.org/web/20240917153554/https://nextjs.org/docs/app/api-reference/next-config-js/env) or define based (https://github.com/vercel/next.js/discussions/71476) approach.
// In order to remove this loader though we need to make sure the minimum supported Next.js version includes this PR (https://github.com/vercel/next.js/pull/61194), otherwise the nextConfig.env based approach will not work, as our SDK code is not processed by Next.js.
function addValueInjectionLoader(
newConfig: WebpackConfigObjectWithModuleRules,
userNextConfig: NextConfigObject,
userSentryOptions: SentryBuildOptions,
buildContext: BuildContext,
releaseName: string | undefined,
routeManifest: RouteManifest | undefined,
): void {
function addValueInjectionLoader({
newConfig,
userNextConfig,
userSentryOptions,
buildContext,
releaseName,
routeManifest,
nextJsVersion,
}: {
newConfig: WebpackConfigObjectWithModuleRules;
userNextConfig: NextConfigObject;
userSentryOptions: SentryBuildOptions;
buildContext: BuildContext;
releaseName: string | undefined;
routeManifest: RouteManifest | undefined;
nextJsVersion: string | undefined;
}): void {
const assetPrefix = userNextConfig.assetPrefix || userNextConfig.basePath || '';

// Check if release creation is disabled to prevent injection that breaks build determinism
Expand Down Expand Up @@ -717,6 +735,7 @@ function addValueInjectionLoader(
// Make sure that if we have a windows path, the backslashes are interpreted as such (rather than as escape
// characters)
_sentryRewriteFramesDistDir: userNextConfig.distDir?.replace(/\\/g, '\\\\') || '.next',
_sentryNextJsVersion: nextJsVersion || '0.0.0',
};

const clientValues = {
Expand Down
9 changes: 8 additions & 1 deletion packages/nextjs/src/config/withSentryConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -314,12 +314,19 @@ function getFinalConfigObject(
webpack:
isTurbopack || userSentryOptions.disableSentryWebpackConfig
? incomingUserNextConfigObject.webpack // just return the original webpack config
: constructWebpackConfigFunction(incomingUserNextConfigObject, userSentryOptions, releaseName, routeManifest),
: constructWebpackConfigFunction(
incomingUserNextConfigObject,
userSentryOptions,
releaseName,
routeManifest,
nextJsVersion,
),
...(isTurbopackSupported && isTurbopack
? {
turbopack: constructTurbopackConfig({
userNextConfig: incomingUserNextConfigObject,
routeManifest,
nextJsVersion,
}),
}
: {}),
Expand Down
Loading
Loading