diff --git a/packages/nextjs/src/config/wrappers/wrapperUtils.ts b/packages/nextjs/src/config/wrappers/wrapperUtils.ts index df546a018377..e5aef5a6ff21 100644 --- a/packages/nextjs/src/config/wrappers/wrapperUtils.ts +++ b/packages/nextjs/src/config/wrappers/wrapperUtils.ts @@ -98,6 +98,7 @@ export function callTracedServerSideDataFetcher Pr op: 'nextjs.data.server', name: options.requestedRouteName, ...traceparentData, + status: 'ok', metadata: { source: 'route', dynamicSamplingContext: traceparentData && !dynamicSamplingContext ? {} : dynamicSamplingContext, @@ -116,6 +117,7 @@ export function callTracedServerSideDataFetcher Pr const dataFetcherSpan = requestTransaction.startChild({ op: 'nextjs.data.server', description: `${options.dataFetchingMethodName} (${options.dataFetcherRouteName})`, + status: 'ok', }); const currentScope = getCurrentHub().getScope(); @@ -137,6 +139,17 @@ export function callTracedServerSideDataFetcher Pr try { return await origFunction(...origFunctionArguments); + } catch (e) { + // Since we finish the span before the error can bubble up and trigger the handlers in `registerErrorInstrumentation` + // that set the transaction status, we need to manually set the status of the span & transaction + dataFetcherSpan.setStatus('internal_error'); + + const transaction = dataFetcherSpan.transaction; + if (transaction) { + transaction.setStatus('internal_error'); + } + + throw e; } finally { dataFetcherSpan.finish(); } @@ -178,14 +191,17 @@ export async function callDataFetcherTraced Promis const span = transaction.startChild({ op: 'nextjs.data.server', description: `${dataFetchingMethodName} (${parameterizedRoute})`, + status: 'ok', }); try { return await origFunction(...origFunctionArgs); } catch (err) { - if (span) { - span.finish(); - } + // Since we finish the span before the error can bubble up and trigger the handlers in `registerErrorInstrumentation` + // that set the transaction status, we need to manually set the status of the span & transaction + transaction.setStatus('internal_error'); + span.setStatus('internal_error'); + span.finish(); // TODO Copy more robust error handling over from `withSentry` captureException(err); diff --git a/packages/nextjs/test/integration/test/server/errorServerSideProps.js b/packages/nextjs/test/integration/test/server/errorServerSideProps.js index ab5f743486b5..1ad531b3e720 100644 --- a/packages/nextjs/test/integration/test/server/errorServerSideProps.js +++ b/packages/nextjs/test/integration/test/server/errorServerSideProps.js @@ -1,7 +1,7 @@ const assert = require('assert'); const { sleep } = require('../utils/common'); -const { getAsync, interceptEventRequest } = require('../utils/server'); +const { getAsync, interceptEventRequest, interceptTracingRequest } = require('../utils/server'); module.exports = async ({ url: urlBase, argv }) => { const url = `${urlBase}/withErrorServerSideProps`; @@ -28,8 +28,32 @@ module.exports = async ({ url: urlBase, argv }) => { 'errorServerSideProps', ); + const capturedTransactionRequest = interceptTracingRequest( + { + contexts: { + trace: { + op: 'nextjs.data.server', + status: 'internal_error', + }, + }, + transaction: '/withErrorServerSideProps', + transaction_info: { + source: 'route', + changes: [], + propagations: 0, + }, + type: 'transaction', + request: { + url, + }, + }, + argv, + 'errorServerSideProps', + ); + await getAsync(url); await sleep(250); assert.ok(capturedRequest.isDone(), 'Did not intercept expected request'); + assert.ok(capturedTransactionRequest.isDone(), 'Did not intercept expected transaction request'); }; diff --git a/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.js b/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.js index ecde33f049c1..95cac78ed649 100644 --- a/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.js +++ b/packages/nextjs/test/integration/test/server/tracingServerGetInitialProps.js @@ -11,6 +11,7 @@ module.exports = async ({ url: urlBase, argv }) => { contexts: { trace: { op: 'nextjs.data.server', + status: 'ok', }, }, transaction: '/[id]/withInitialProps', diff --git a/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.js b/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.js index 42edeb6842bf..16f10d1688bd 100644 --- a/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.js +++ b/packages/nextjs/test/integration/test/server/tracingServerGetServerSideProps.js @@ -11,6 +11,7 @@ module.exports = async ({ url: urlBase, argv }) => { contexts: { trace: { op: 'nextjs.data.server', + status: 'ok', }, }, transaction: '/[id]/withServerSideProps',