Is there an existing issue for this?
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/nextjs
SDK Version
10.73.0
Framework Version
Next.js 16.3.0 (Turbopack)
Link to Sentry event
No response
Reproduction Example/SDK Setup
next.config.ts:
import { withSentryConfig } from '@sentry/nextjs';
export default withSentryConfig(
{
output: 'standalone',
// Turbopack's native debug ID generation
turbopack: { debugIds: true },
serverExternalPackages: ['@sentry/profiling-node'],
},
{
org: process.env.SENTRY_ORG,
project: process.env.SENTRY_PROJECT,
authToken: process.env.SENTRY_AUTH_TOKEN,
release: { name: process.env.SENTRY_RELEASE },
widenClientFileUpload: true,
sourcemaps: { deleteSourcemapsAfterUpload: false },
},
);
instrumentation.ts:
import * as Sentry from '@sentry/nextjs';
import { nodeProfilingIntegration } from '@sentry/profiling-node';
Sentry.init({
dsn: process.env.SENTRY_DSN,
tracesSampleRate: 1.0,
profileSessionSampleRate: 1.0,
profileLifecycle: 'trace',
integrations: [nodeProfilingIntegration()],
});
Steps to Reproduce
- Build a Next.js 16 app with Turbopack,
turbopack: { debugIds: true } and output: 'standalone', using @sentry/nextjs and @sentry/profiling-node 10.73.0 with server-side continuous profiling.
- Build for production and deploy the standalone output. Source map upload runs through
runAfterProductionCompile and the emitted server chunks carry both a //# debugId= comment and the native registration snippet that writes globalThis._debugIds.
- Send traffic so the same process produces both an error event and a profile chunk.
- Compare
debug_meta on the error event with debug_meta on the profile chunk, and open the flamegraph.
Expected Result
The profile chunk should carry a populated debug_meta.images, the same way the error event does, so the flamegraph resolves to original file paths and function names.
Actual Result
The error event symbolicates correctly. Its debug_meta contains the right image for the server chunk:
{
"images": [
{
"code_file": "app:///_next/server/chunks/[root-of-the-server]__aaaaaaa._.js",
"debug_id": "00000000-0000-0000-0000-000000000000",
"type": "sourcemap"
}
]
}
The profile chunk from the same process, same release and same server chunk arrives with:
Flamegraph frames stay as minified identifiers (oV, aN, eQ) with raw chunk paths such as /srv/app/.next/server/chunks/[root-of-the-server]__aaaaaaa._.js:11:64839. Because debug_meta is empty, the backend skips profile symbolication entirely rather than failing at it.
Additional Context
With turbopack.debugIds enabled, @sentry/nextjs treats debug IDs as natively provided and skips injectDebugIds in handleRunAfterProductionCompile.ts. Nothing writes globalThis._sentryDebugIds in that case, only Turbopack's globalThis._debugIds. Error events are unaffected because getFilenameToDebugIdMap in packages/core/src/utils/debug-ids.ts reads both globals.
The profiler doesn't. @sentry/node-cpu-profiler 2.4.3 decides whether to collect script resources from the legacy global alone:
// lib/index.js
return PrivateCpuProfilerBindings.stopProfiling(name, format, threadId, !!global._sentryDebugIds);
That argument is collect_resources in the binding, and TranslateProfile substitutes an empty array for resources when it is false. createProfileChunkPayload derives the images from exactly that array via applyDebugMetadata(client, cpuProfile.resources), so the chunk leaves the process with nothing to symbolicate.
Resolution itself is fine. Taking the registration snippet out of a real server chunk and passing that chunk's own path through getDebugImagesForResources returns the correct image, so the gate looks like the only broken part.
Setting globalThis._sentryDebugIds = globalThis._sentryDebugIds || {} before Sentry.init works around it, since the empty object passes the check and the merged map still resolves the native IDs.
Worth noting separately that frames from ESM packages under node_modules arrive as file:///... paths, which the backend rejects per getsentry/sentry#109520. Turbopack's own server chunks use plain absolute paths and aren't affected by that one.
Priority
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding +1 or me too, to help us triage it.
Is there an existing issue for this?
How do you use Sentry?
Sentry Saas (sentry.io)
Which SDK are you using?
@sentry/nextjs
SDK Version
10.73.0
Framework Version
Next.js 16.3.0 (Turbopack)
Link to Sentry event
No response
Reproduction Example/SDK Setup
next.config.ts:instrumentation.ts:Steps to Reproduce
turbopack: { debugIds: true }andoutput: 'standalone', using@sentry/nextjsand@sentry/profiling-node10.73.0 with server-side continuous profiling.runAfterProductionCompileand the emitted server chunks carry both a//# debugId=comment and the native registration snippet that writesglobalThis._debugIds.debug_metaon the error event withdebug_metaon the profile chunk, and open the flamegraph.Expected Result
The profile chunk should carry a populated
debug_meta.images, the same way the error event does, so the flamegraph resolves to original file paths and function names.Actual Result
The error event symbolicates correctly. Its
debug_metacontains the right image for the server chunk:{ "images": [ { "code_file": "app:///_next/server/chunks/[root-of-the-server]__aaaaaaa._.js", "debug_id": "00000000-0000-0000-0000-000000000000", "type": "sourcemap" } ] }The profile chunk from the same process, same release and same server chunk arrives with:
"debug_meta": {}Flamegraph frames stay as minified identifiers (
oV,aN,eQ) with raw chunk paths such as/srv/app/.next/server/chunks/[root-of-the-server]__aaaaaaa._.js:11:64839. Becausedebug_metais empty, the backend skips profile symbolication entirely rather than failing at it.Additional Context
With
turbopack.debugIdsenabled,@sentry/nextjstreats debug IDs as natively provided and skipsinjectDebugIdsinhandleRunAfterProductionCompile.ts. Nothing writesglobalThis._sentryDebugIdsin that case, only Turbopack'sglobalThis._debugIds. Error events are unaffected becausegetFilenameToDebugIdMapinpackages/core/src/utils/debug-ids.tsreads both globals.The profiler doesn't.
@sentry/node-cpu-profiler2.4.3 decides whether to collect script resources from the legacy global alone:That argument is
collect_resourcesin the binding, andTranslateProfilesubstitutes an empty array forresourceswhen it is false.createProfileChunkPayloadderives the images from exactly that array viaapplyDebugMetadata(client, cpuProfile.resources), so the chunk leaves the process with nothing to symbolicate.Resolution itself is fine. Taking the registration snippet out of a real server chunk and passing that chunk's own path through
getDebugImagesForResourcesreturns the correct image, so the gate looks like the only broken part.Setting
globalThis._sentryDebugIds = globalThis._sentryDebugIds || {}beforeSentry.initworks around it, since the empty object passes the check and the merged map still resolves the native IDs.Worth noting separately that frames from ESM packages under
node_modulesarrive asfile:///...paths, which the backend rejects per getsentry/sentry#109520. Turbopack's own server chunks use plain absolute paths and aren't affected by that one.Priority
React with 👍 to help prioritize this issue. Please use comments to provide useful context, avoiding
+1orme too, to help us triage it.