Skip to content

Commit e8dfc58

Browse files
nicohrubeccodex
andauthored
fix(v10/nitro): Capture errors only through the Nitro error hook (#24963)
Backport of: #24952 ## Differences to the original PR - The 400-error regression test waits for the flush endpoint's transaction instead of a streamed span, matching v10's default trace lifecycle. - The standard error test checks the hook mechanism on the original `Error` instead of the `HTTPError` wrapper, matching v10's mechanism assignment within linked errors. Fixes #24939 Co-authored-by: OpenAI Codex <codex@openai.com>
1 parent 3a57cd3 commit e8dfc58

4 files changed

Lines changed: 52 additions & 23 deletions

File tree

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
import { flush } from '@sentry/nitro';
2+
import { defineHandler } from 'nitro/h3';
3+
4+
export default defineHandler(async () => {
5+
await flush();
6+
return { status: 'ok' };
7+
});
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
import { defineHandler, HTTPError } from 'nitro/h3';
2+
3+
export default defineHandler(() => {
4+
throw new HTTPError({ status: 400, message: 'Explicit 400 test error' });
5+
});

‎dev-packages/e2e-tests/test-applications/nitro-3/tests/errors.test.ts‎

Lines changed: 40 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,10 @@
11
import { expect, test } from '@playwright/test';
2-
import { waitForError } from '@sentry-internal/test-utils';
2+
import { waitForError, waitForTransaction } from '@sentry-internal/test-utils';
33

44
test('Sends an error event to Sentry', async ({ request }) => {
5-
// The thrown error is reported twice: once via the h3 tracing channel and once via Nitro's `error`
6-
// hook (which sees it wrapped in an `HTTPError`). Match on the mechanism so we deterministically
7-
// await the event under test instead of whichever arrives first.
85
const errorEventPromise = waitForError('nitro-3', event => {
9-
return (
10-
!event.type &&
11-
!!event.exception?.values?.some(
12-
v => v.value === 'This is a test error' && v.mechanism?.type === 'auto.http.nitro.onTraceError',
13-
)
6+
return !!event.exception?.values?.some(
7+
v => v.value === 'This is a test error' && v.mechanism?.type === 'auto.function.nitro.captureErrorHook',
148
);
159
});
1610

@@ -20,16 +14,45 @@ test('Sends an error event to Sentry', async ({ request }) => {
2014

2115
const errorEvent = await errorEventPromise;
2216

23-
expect(errorEvent.exception?.values).toHaveLength(1);
24-
25-
expect(errorEvent.exception?.values?.[0]?.type).toBe('Error');
26-
expect(errorEvent.exception?.values?.[0]?.value).toBe('This is a test error');
27-
expect(errorEvent.exception?.values?.[0]?.mechanism).toEqual(
17+
expect(errorEvent.exception?.values).toEqual([
18+
expect.objectContaining({
19+
type: 'Error',
20+
value: 'This is a test error',
21+
mechanism: expect.objectContaining({
22+
handled: false,
23+
type: 'auto.function.nitro.captureErrorHook',
24+
}),
25+
}),
2826
expect.objectContaining({
29-
handled: false,
30-
type: 'auto.http.nitro.onTraceError',
27+
type: 'HTTPError',
28+
value: 'This is a test error',
3129
}),
32-
);
30+
]);
31+
});
32+
33+
test('Does not send an explicitly thrown 400 error to Sentry', async ({ request }) => {
34+
let errorReceived = false;
35+
36+
void waitForError('nitro-3', event => {
37+
if (event.exception?.values?.some(v => v.value === 'Explicit 400 test error')) {
38+
errorReceived = true;
39+
return true;
40+
}
41+
return false;
42+
});
43+
44+
const flushTransactionPromise = waitForTransaction('nitro-3', event => {
45+
return event.transaction === 'GET /api/flush';
46+
});
47+
48+
const response = await request.get('/api/test-error-400');
49+
expect(response.status()).toBe(400);
50+
51+
const flushResponse = await request.get('/api/flush');
52+
expect(flushResponse.status()).toBe(200);
53+
await flushTransactionPromise;
54+
55+
expect(errorReceived).toBe(false);
3356
});
3457

3558
test('Does not send 404 errors to Sentry', async ({ request }) => {

‎packages/nitro/src/runtime/hooks/captureTracingEvents.ts‎

Lines changed: 0 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -115,12 +115,6 @@ function setupH3TracingChannels(): void {
115115
return span;
116116
},
117117
{
118-
captureError: () => ({
119-
mechanism: {
120-
handled: false,
121-
type: 'auto.http.nitro.onTraceError',
122-
},
123-
}),
124118
beforeSpanEnd(span, data) {
125119
applyResponseStatus(span, data);
126120

0 commit comments

Comments
 (0)