Skip to content

Commit 0d558de

Browse files
authored
feat(nestjs): Filter 4xx errors (#12695)
Small change filtering expected errors from being reported to sentry in the nestjs sdk. Tested manually on my sample app. Fixes #12523
1 parent 3d4a3e0 commit 0d558de

File tree

4 files changed

+43
-2
lines changed

4 files changed

+43
-2
lines changed

dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,11 @@ export class AppController1 {
4545
return this.appService.testException(id);
4646
}
4747

48+
@Get('test-expected-exception/:id')
49+
async testExpectedException(@Param('id') id: string) {
50+
return this.appService.testExpectedException(id);
51+
}
52+
4853
@Get('test-outgoing-fetch-external-allowed')
4954
async testOutgoingFetchExternalAllowed() {
5055
return this.appService.testOutgoingFetchExternalAllowed();

dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Injectable } from '@nestjs/common';
1+
import { HttpException, HttpStatus, Injectable } from '@nestjs/common';
22
import * as Sentry from '@sentry/nestjs';
33
import { makeHttpRequest } from './utils';
44

@@ -52,6 +52,10 @@ export class AppService1 {
5252
throw new Error(`This is an exception with id ${id}`);
5353
}
5454

55+
testExpectedException(id: string) {
56+
throw new HttpException(`This is an expected exception with id ${id}`, HttpStatus.FORBIDDEN);
57+
}
58+
5559
async testOutgoingFetchExternalAllowed() {
5660
const fetchResponse = await fetch('http://localhost:3040/external-allowed');
5761

dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts

Lines changed: 26 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
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 exception to Sentry', async ({ baseURL }) => {
55
const errorEventPromise = waitForError('nestjs', event => {
@@ -28,3 +28,28 @@ test('Sends exception to Sentry', async ({ baseURL }) => {
2828
span_id: expect.any(String),
2929
});
3030
});
31+
32+
test('Does not send expected exception to Sentry', async ({ baseURL }) => {
33+
let errorEventOccurred = false;
34+
35+
waitForError('nestjs', event => {
36+
if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected exception with id 123') {
37+
errorEventOccurred = true;
38+
}
39+
40+
return event?.transaction === 'GET /test-expected-exception/:id';
41+
});
42+
43+
const transactionEventPromise = waitForTransaction('nestjs', transactionEvent => {
44+
return transactionEvent?.transaction === 'GET /test-expected-exception/:id';
45+
});
46+
47+
const response = await fetch(`${baseURL}/test-expected-exception/123`);
48+
expect(response.status).toBe(403);
49+
50+
await transactionEventPromise;
51+
52+
await new Promise(resolve => setTimeout(resolve, 10000));
53+
54+
expect(errorEventOccurred).toBe(false);
55+
});

packages/node/src/integrations/tracing/nest.ts

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,13 @@ export function setupNestErrorHandler(app: MinimalNestJsApp, baseFilter: NestJsE
100100
const originalCatch = Reflect.get(target, prop, receiver);
101101

102102
return (exception: unknown, host: unknown) => {
103+
const status_code = (exception as { status?: number }).status;
104+
105+
// don't report expected errors
106+
if (status_code !== undefined && status_code >= 400 && status_code < 500) {
107+
return originalCatch.apply(target, [exception, host]);
108+
}
109+
103110
captureException(exception);
104111
return originalCatch.apply(target, [exception, host]);
105112
};

0 commit comments

Comments
 (0)