diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts index 6350cb49f1c5..154f62ada912 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.controller.ts @@ -45,6 +45,11 @@ export class AppController1 { return this.appService.testException(id); } + @Get('test-expected-exception/:id') + async testExpectedException(@Param('id') id: string) { + return this.appService.testExpectedException(id); + } + @Get('test-outgoing-fetch-external-allowed') async testOutgoingFetchExternalAllowed() { return this.appService.testOutgoingFetchExternalAllowed(); diff --git a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts index 01a96549546b..1103c65941a1 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/src/app.service.ts @@ -1,4 +1,4 @@ -import { Injectable } from '@nestjs/common'; +import { HttpException, HttpStatus, Injectable } from '@nestjs/common'; import * as Sentry from '@sentry/nestjs'; import { makeHttpRequest } from './utils'; @@ -52,6 +52,10 @@ export class AppService1 { throw new Error(`This is an exception with id ${id}`); } + testExpectedException(id: string) { + throw new HttpException(`This is an expected exception with id ${id}`, HttpStatus.FORBIDDEN); + } + async testOutgoingFetchExternalAllowed() { const fetchResponse = await fetch('http://localhost:3040/external-allowed'); diff --git a/dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts b/dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts index aa46f77815d4..349b25b0eee9 100644 --- a/dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts +++ b/dev-packages/e2e-tests/test-applications/nestjs/tests/errors.test.ts @@ -1,5 +1,5 @@ import { expect, test } from '@playwright/test'; -import { waitForError } from '@sentry-internal/test-utils'; +import { waitForError, waitForTransaction } from '@sentry-internal/test-utils'; test('Sends exception to Sentry', async ({ baseURL }) => { const errorEventPromise = waitForError('nestjs', event => { @@ -28,3 +28,28 @@ test('Sends exception to Sentry', async ({ baseURL }) => { span_id: expect.any(String), }); }); + +test('Does not send expected exception to Sentry', async ({ baseURL }) => { + let errorEventOccurred = false; + + waitForError('nestjs', event => { + if (!event.type && event.exception?.values?.[0]?.value === 'This is an expected exception with id 123') { + errorEventOccurred = true; + } + + return event?.transaction === 'GET /test-expected-exception/:id'; + }); + + const transactionEventPromise = waitForTransaction('nestjs', transactionEvent => { + return transactionEvent?.transaction === 'GET /test-expected-exception/:id'; + }); + + const response = await fetch(`${baseURL}/test-expected-exception/123`); + expect(response.status).toBe(403); + + await transactionEventPromise; + + await new Promise(resolve => setTimeout(resolve, 10000)); + + expect(errorEventOccurred).toBe(false); +}); diff --git a/packages/node/src/integrations/tracing/nest.ts b/packages/node/src/integrations/tracing/nest.ts index bbb658318946..ab6a66fdb895 100644 --- a/packages/node/src/integrations/tracing/nest.ts +++ b/packages/node/src/integrations/tracing/nest.ts @@ -100,6 +100,13 @@ export function setupNestErrorHandler(app: MinimalNestJsApp, baseFilter: NestJsE const originalCatch = Reflect.get(target, prop, receiver); return (exception: unknown, host: unknown) => { + const status_code = (exception as { status?: number }).status; + + // don't report expected errors + if (status_code !== undefined && status_code >= 400 && status_code < 500) { + return originalCatch.apply(target, [exception, host]); + } + captureException(exception); return originalCatch.apply(target, [exception, host]); };