diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/client/click-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/client/click-error.test.ts index a9fbcdb69a45..481e7264c62d 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/client/click-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/client/click-error.test.ts @@ -13,8 +13,18 @@ test('should send error for faulty click handlers', async ({ page }) => { expect(errorEvent).toBeDefined(); - const frames = errorEvent?.exception?.values?.[0]?.stacktrace?.frames; + const exception = errorEvent?.exception?.values?.[0]; + + expect(exception?.mechanism).toEqual({ + type: 'auto.browser.browserapierrors.addEventListener', + handled: false, + data: { + handler: expect.any(String), // the handler name varies in CI and locally + target: 'EventTarget', + }, + }); + const frames = exception?.stacktrace?.frames; await test.step('error should have a non-url-encoded top frame in route with parameter', () => { if (process.env.TEST_ENV === 'development') { // In dev mode we want to check local source mapping diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/getServerSideProps.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/getServerSideProps.test.ts index 60f9842ca20e..3338d8e28595 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/getServerSideProps.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/getServerSideProps.test.ts @@ -23,7 +23,7 @@ test('Should report an error event for errors thrown in getServerSideProps', asy exception: { values: [ { - mechanism: { handled: false, type: 'generic' }, + mechanism: { handled: false, type: 'auto.function.nextjs.wrapped' }, type: 'Error', value: 'getServerSideProps Error', stacktrace: { @@ -110,7 +110,7 @@ test('Should report an error event for errors thrown in getServerSideProps in pa exception: { values: [ { - mechanism: { handled: false, type: 'generic' }, + mechanism: { handled: false, type: 'auto.function.nextjs.wrapped' }, type: 'Error', value: 'custom page extension error', stacktrace: { diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/pages-router-api-endpoints.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/pages-router-api-endpoints.test.ts index 36592b56fb94..9f5ff5db8434 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/pages-router-api-endpoints.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/pages-router-api-endpoints.test.ts @@ -30,7 +30,7 @@ test('Should report an error event for errors thrown in pages router api routes' function: 'withSentry', }, handled: false, - type: 'instrument', + type: 'auto.http.nextjs.api_handler', }, stacktrace: { frames: expect.arrayContaining([]) }, type: 'Error', diff --git a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/server-component-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/server-component-error.test.ts index 937d7c8da6c0..c485c02716a3 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/server-component-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-13/tests/server/server-component-error.test.ts @@ -20,7 +20,7 @@ test('Should capture an error thrown in a server component', async ({ page }) => exception: { values: [ { - mechanism: { handled: false, type: 'generic' }, + mechanism: { handled: false, type: 'auto.function.nextjs.server_component' }, type: 'Error', value: 'RSC error', }, diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts index 863e5de111a2..68d03a00d601 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/nested-rsc-error.test.ts @@ -41,4 +41,9 @@ test('Should capture errors from nested server components when `Sentry.captureRe router_path: '/nested-rsc-error/[param]', request_path: '/nested-rsc-error/123', }); + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nextjs.on_request_error', + }); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts index 0a20e97be74a..6133d0fa29b1 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-15/tests/streaming-rsc-error.test.ts @@ -41,4 +41,9 @@ test('Should capture errors for crashing streaming promises in server components router_path: '/streaming-rsc-error/[param]', request_path: '/streaming-rsc-error/123', }); + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nextjs.on_request_error', + }); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts index 091c8fc1e46c..580368f4b9a1 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/client-errors.test.ts @@ -35,4 +35,15 @@ test('Sends a client-side exception to Sentry', async ({ page }) => { trace_id: expect.stringMatching(/[a-f0-9]{32}/), span_id: expect.stringMatching(/[a-f0-9]{16}/), }); + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: nextjsMajor >= 15 ? 'auto.browser.global_handlers.onerror' : 'auto.browser.browserapierrors.addEventListener', + ...(nextjsMajor < 15 && { + data: { + handler: expect.any(String), // the handler name varies in CI and locally + target: 'EventTarget', + }, + }), + }); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts index 934cfa2e472d..a19fe3402951 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-app-dir/tests/edge.test.ts @@ -19,6 +19,11 @@ test('Should record exceptions for faulty edge server components', async ({ page expect(errorEvent.tags?.['my-global-scope-isolated-tag']).not.toBeDefined(); expect(errorEvent.transaction).toBe(`Page Server Component (/edge-server-components/error)`); + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nextjs.server_component', + }); }); test('Should record transaction for edge server components', async ({ page }) => { diff --git a/dev-packages/e2e-tests/test-applications/nextjs-orpc/tests/orpc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-orpc/tests/orpc-error.test.ts index 8a9f371972c0..a503533b6f00 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-orpc/tests/orpc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-orpc/tests/orpc-error.test.ts @@ -19,4 +19,10 @@ test('should capture orpc error', async ({ page }) => { }), ], }); + + // orpc errors are captured manually by the orpc middleware (user-land) + expect(orpcError.exception?.values?.[0]?.mechanism).toEqual({ + handled: true, + type: 'generic', + }); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/pages-ssr-errors.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/pages-ssr-errors.test.ts index c3925f52ba48..e5539244570f 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/pages-ssr-errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-pages-dir/tests/pages-ssr-errors.test.ts @@ -43,4 +43,9 @@ test('Will capture error for SSR rendering error with a connected trace (Functio // TODO(lforst): Reuse SSR request span isolation scope to fix the following two assertions // expect(ssrTransaction.tags?.['my-isolated-tag']).toBe(true); // expect(ssrTransaction.tags?.['my-global-scope-isolated-tag']).not.toBeDefined(); + + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nextjs.page_function', + }); }); diff --git a/dev-packages/e2e-tests/test-applications/nextjs-t3/tests/trpc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-t3/tests/trpc-error.test.ts index d3e175e0558b..2a7af1b14d52 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-t3/tests/trpc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-t3/tests/trpc-error.test.ts @@ -16,6 +16,14 @@ test('should capture error with trpc context', async ({ page }) => { expect(trpcError.contexts?.trpc?.procedure_type).toEqual('mutation'); expect(trpcError.contexts?.trpc?.procedure_path).toBe('post.throwError'); expect(trpcError.contexts?.trpc?.input).toEqual({ name: 'I love dogs' }); + + expect(trpcError.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.rpc.trpc.middleware', + exception_id: 1, + parent_id: 0, + source: 'cause', + }); }); test('should create transaction with trpc input for error', async ({ page }) => { diff --git a/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/app-router/rsc-error.test.ts b/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/app-router/rsc-error.test.ts index 604faae7ea59..d9c94de2d2be 100644 --- a/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/app-router/rsc-error.test.ts +++ b/dev-packages/e2e-tests/test-applications/nextjs-turbo/tests/app-router/rsc-error.test.ts @@ -11,4 +11,8 @@ test('Should capture errors from server components', async ({ page }) => { const errorEvent = await errorEventPromise; expect(errorEvent).toBeDefined(); + expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual({ + handled: false, + type: 'auto.function.nextjs.on_request_error', + }); }); diff --git a/packages/nextjs/src/common/captureRequestError.ts b/packages/nextjs/src/common/captureRequestError.ts index fec9d46d0e65..6fd2e83d3188 100644 --- a/packages/nextjs/src/common/captureRequestError.ts +++ b/packages/nextjs/src/common/captureRequestError.ts @@ -38,6 +38,7 @@ export function captureRequestError(error: unknown, request: RequestInfo, errorC captureException(error, { mechanism: { handled: false, + type: 'auto.function.nextjs.on_request_error', }, }); diff --git a/packages/nextjs/src/common/pages-router-instrumentation/_error.ts b/packages/nextjs/src/common/pages-router-instrumentation/_error.ts index b33c648839fa..354fbe0438e2 100644 --- a/packages/nextjs/src/common/pages-router-instrumentation/_error.ts +++ b/packages/nextjs/src/common/pages-router-instrumentation/_error.ts @@ -45,7 +45,7 @@ export async function captureUnderscoreErrorException(contextOrProps: ContextOrP // is what passing a string to `captureException` will wind up doing) captureException(err || `_error.js called with falsy error (${err})`, { mechanism: { - type: 'instrument', + type: 'auto.function.nextjs.underscore_error', handled: false, data: { function: '_error.getInitialProps', diff --git a/packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts index a0e4112404cd..4cf8fde751fb 100644 --- a/packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts +++ b/packages/nextjs/src/common/pages-router-instrumentation/wrapApiHandlerWithSentry.ts @@ -112,7 +112,7 @@ export function wrapApiHandlerWithSentry(apiHandler: NextApiHandler, parameteriz captureException(objectifiedErr, { mechanism: { - type: 'instrument', + type: 'auto.http.nextjs.api_handler', handled: false, data: { wrapped_handler: wrappingTarget.name, diff --git a/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts b/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts index b08bdad5e9ab..693341024726 100644 --- a/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts +++ b/packages/nextjs/src/common/pages-router-instrumentation/wrapPageComponentWithSentry.ts @@ -48,6 +48,7 @@ export function wrapPageComponentWithSentry(pageComponent: FunctionComponent | C captureException(e, { mechanism: { handled: false, + type: 'auto.function.nextjs.page_class', }, }); throw e; @@ -77,6 +78,7 @@ export function wrapPageComponentWithSentry(pageComponent: FunctionComponent | C captureException(e, { mechanism: { handled: false, + type: 'auto.function.nextjs.page_function', }, }); throw e; diff --git a/packages/nextjs/src/common/utils/wrapperUtils.ts b/packages/nextjs/src/common/utils/wrapperUtils.ts index 23b960c857cf..5bde1f76f0d4 100644 --- a/packages/nextjs/src/common/utils/wrapperUtils.ts +++ b/packages/nextjs/src/common/utils/wrapperUtils.ts @@ -25,7 +25,11 @@ export function withErrorInstrumentation any>( return await origFunction.apply(this, origFunctionArguments); } catch (e) { // TODO: Extract error logic from `withSentry` in here or create a new wrapper with said logic or something like that. - captureException(e, { mechanism: { handled: false } }); + captureException(e, { + // TODO: check if origFunction.name actually returns the correct name or minified garbage + // in this case, we can add another argument to this wrapper with the respective function name + mechanism: { handled: false, type: 'auto.function.nextjs.wrapped', data: { function: origFunction.name } }, + }); throw e; } }; @@ -99,7 +103,7 @@ export async function callDataFetcherTraced Promis try { return await origFunction(...origFunctionArgs); } catch (e) { - captureException(e, { mechanism: { handled: false } }); + captureException(e, { mechanism: { handled: false, type: 'auto.function.nextjs.data_fetcher' } }); throw e; } } diff --git a/packages/nextjs/src/common/withServerActionInstrumentation.ts b/packages/nextjs/src/common/withServerActionInstrumentation.ts index e3cc2831d5e4..f5a1170cc44b 100644 --- a/packages/nextjs/src/common/withServerActionInstrumentation.ts +++ b/packages/nextjs/src/common/withServerActionInstrumentation.ts @@ -7,6 +7,7 @@ import { getClient, getIsolationScope, handleCallbackErrors, + SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN, SEMANTIC_ATTRIBUTE_SENTRY_SOURCE, SPAN_STATUS_ERROR, startSpan, @@ -116,6 +117,7 @@ async function withServerActionInstrumentationImplementation { @@ -130,6 +132,7 @@ async function withServerActionInstrumentationImplementation a captureException(err, { mechanism: { handled: false, + type: 'auto.function.nextjs.generation_function', + data: { + function: generationFunctionIdentifier, + }, }, }); } diff --git a/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts index bd84fb4195b7..ab05fbd5e944 100644 --- a/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts +++ b/packages/nextjs/src/common/wrapMiddlewareWithSentry.ts @@ -98,7 +98,7 @@ export function wrapMiddlewareWithSentry( op: 'http.server.middleware', attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: spanSource, - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrapMiddlewareWithSentry', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrap_middleware', ...headerAttributes, }, }, @@ -108,7 +108,7 @@ export function wrapMiddlewareWithSentry( error => { captureException(error, { mechanism: { - type: 'instrument', + type: 'auto.function.nextjs.wrap_middleware', handled: false, }, }); diff --git a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts index e10d51321a0e..54858a9bdae2 100644 --- a/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts +++ b/packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts @@ -96,6 +96,7 @@ export function wrapRouteHandlerWithSentry any>( captureException(error, { mechanism: { handled: false, + type: 'auto.function.nextjs.route_handler', }, }); } diff --git a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts index f0f8e9df8717..d25225a149f9 100644 --- a/packages/nextjs/src/common/wrapServerComponentWithSentry.ts +++ b/packages/nextjs/src/common/wrapServerComponentWithSentry.ts @@ -114,7 +114,7 @@ export function wrapServerComponentWithSentry any> name: `${componentType} Server Component (${componentRoute})`, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'component', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.server_component', 'sentry.nextjs.ssr.function.type': componentType, 'sentry.nextjs.ssr.function.route': componentRoute, }, @@ -136,6 +136,7 @@ export function wrapServerComponentWithSentry any> captureException(error, { mechanism: { handled: false, + type: 'auto.function.nextjs.server_component', }, }); } diff --git a/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts b/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts index 6002981cfcf4..735caf8d7788 100644 --- a/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts +++ b/packages/nextjs/src/edge/wrapApiHandlerWithSentry.ts @@ -79,7 +79,7 @@ export function wrapApiHandlerWithSentry( op: op, attributes: { [SEMANTIC_ATTRIBUTE_SENTRY_SOURCE]: 'route', - [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrapApiHandlerWithSentry', + [SEMANTIC_ATTRIBUTE_SENTRY_ORIGIN]: 'auto.function.nextjs.wrap_api_handler', ...headerAttributes, }, }, @@ -89,7 +89,7 @@ export function wrapApiHandlerWithSentry( error => { captureException(error, { mechanism: { - type: 'instrument', + type: 'auto.function.nextjs.wrap_api_handler', handled: false, }, });