Skip to content

Commit be32c1e

Browse files
authored
fix(nextjs): Catch rejecting flushes (#9811)
1 parent 7303cda commit be32c1e

File tree

5 files changed

+24
-23
lines changed

5 files changed

+24
-23
lines changed

packages/nextjs/src/common/_error.ts

+2-7
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
import { captureException, getClient, withScope } from '@sentry/core';
22
import type { NextPageContext } from 'next';
3+
import { flushQueue } from './utils/responseEnd';
34

45
type ContextOrProps = {
56
req?: NextPageContext['req'];
@@ -9,12 +10,6 @@ type ContextOrProps = {
910
statusCode?: number;
1011
};
1112

12-
/** Platform-agnostic version of `flush` */
13-
function flush(timeout?: number): PromiseLike<boolean> {
14-
const client = getClient();
15-
return client ? client.flush(timeout) : Promise.resolve(false);
16-
}
17-
1813
/**
1914
* Capture the exception passed by nextjs to the `_error` page, adding context data as appropriate.
2015
*
@@ -60,5 +55,5 @@ export async function captureUnderscoreErrorException(contextOrProps: ContextOrP
6055

6156
// In case this is being run as part of a serverless function (as is the case with the server half of nextjs apps
6257
// deployed to vercel), make sure the error gets sent to Sentry before the lambda exits.
63-
await flush(2000);
58+
await flushQueue();
6459
}

packages/nextjs/src/common/utils/edgeWrapperUtils.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@ import {
1010

1111
import type { EdgeRouteHandler } from '../../edge/types';
1212
import { DEBUG_BUILD } from '../debug-build';
13+
import { flushQueue } from './responseEnd';
1314

1415
/**
1516
* Wraps a function on the edge runtime with error and performance monitoring.
@@ -97,7 +98,7 @@ export function withEdgeWrapping<H extends EdgeRouteHandler>(
9798
} finally {
9899
span?.finish();
99100
currentScope?.setSpan(prevSpan);
100-
await flush(2000);
101+
await flushQueue();
101102
}
102103
};
103104
}

packages/nextjs/src/common/withServerActionInstrumentation.ts

+3-2
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ import { logger, tracingContextFromHeaders } from '@sentry/utils';
33

44
import { DEBUG_BUILD } from './debug-build';
55
import { platformSupportsStreaming } from './utils/platformSupportsStreaming';
6+
import { flushQueue } from './utils/responseEnd';
67

78
interface Options {
89
formData?: FormData;
@@ -118,11 +119,11 @@ async function withServerActionInstrumentationImplementation<A extends (...args:
118119
} finally {
119120
if (!platformSupportsStreaming()) {
120121
// Lambdas require manual flushing to prevent execution freeze before the event is sent
121-
await flush(1000);
122+
await flushQueue();
122123
}
123124

124125
if (process.env.NEXT_RUNTIME === 'edge') {
125-
void flush();
126+
void flushQueue();
126127
}
127128
}
128129

packages/nextjs/src/common/wrapRouteHandlerWithSentry.ts

+2-1
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@ import { tracingContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils'
44
import { isRedirectNavigationError } from './nextNavigationErrorUtils';
55
import type { RouteHandlerContext } from './types';
66
import { platformSupportsStreaming } from './utils/platformSupportsStreaming';
7+
import { flushQueue } from './utils/responseEnd';
78

89
/**
910
* Wraps a Next.js route handler with performance and error instrumentation.
@@ -70,7 +71,7 @@ export function wrapRouteHandlerWithSentry<F extends (...args: any[]) => any>(
7071
if (!platformSupportsStreaming() || process.env.NEXT_RUNTIME === 'edge') {
7172
// 1. Edge tranpsort requires manual flushing
7273
// 2. Lambdas require manual flushing to prevent execution freeze before the event is sent
73-
await flush(1000);
74+
await flushQueue();
7475
}
7576
}
7677

packages/nextjs/src/common/wrapServerComponentWithSentry.ts

+15-12
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
import {
22
addTracingExtensions,
33
captureException,
4-
flush,
54
getCurrentHub,
65
runWithAsyncContext,
76
startTransaction,
@@ -10,6 +9,7 @@ import { tracingContextFromHeaders, winterCGHeadersToDict } from '@sentry/utils'
109

1110
import { isNotFoundNavigationError, isRedirectNavigationError } from '../common/nextNavigationErrorUtils';
1211
import type { ServerComponentContext } from '../common/types';
12+
import { flushQueue } from './utils/responseEnd';
1313

1414
/**
1515
* Wraps an `app` directory server component with Sentry error instrumentation.
@@ -85,28 +85,31 @@ export function wrapServerComponentWithSentry<F extends (...args: any[]) => any>
8585
maybePromiseResult = originalFunction.apply(thisArg, args);
8686
} catch (e) {
8787
handleErrorCase(e);
88-
void flush();
88+
void flushQueue();
8989
throw e;
9090
}
9191

9292
if (typeof maybePromiseResult === 'object' && maybePromiseResult !== null && 'then' in maybePromiseResult) {
9393
// eslint-disable-next-line @typescript-eslint/no-unsafe-member-access
94-
Promise.resolve(maybePromiseResult).then(
95-
() => {
96-
transaction.finish();
97-
},
98-
e => {
99-
handleErrorCase(e);
100-
},
101-
);
102-
void flush();
94+
Promise.resolve(maybePromiseResult)
95+
.then(
96+
() => {
97+
transaction.finish();
98+
},
99+
e => {
100+
handleErrorCase(e);
101+
},
102+
)
103+
.finally(() => {
104+
void flushQueue();
105+
});
103106

104107
// It is very important that we return the original promise here, because Next.js attaches various properties
105108
// to that promise and will throw if they are not on the returned value.
106109
return maybePromiseResult;
107110
} else {
108111
transaction.finish();
109-
void flush();
112+
void flushQueue();
110113
return maybePromiseResult;
111114
}
112115
});

0 commit comments

Comments
 (0)