From 171aad50bd182b50a4f954755b0d46fe881d7989 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 9 Jul 2025 10:30:09 +0200 Subject: [PATCH 1/2] feat(cloudflare): Add option to opt out of capturing errors in `wrapRequesHandler` --- packages/cloudflare/src/request.ts | 16 +++++++++++--- packages/cloudflare/test/request.test.ts | 27 ++++++++++++++++++++++++ 2 files changed, 40 insertions(+), 3 deletions(-) diff --git a/packages/cloudflare/src/request.ts b/packages/cloudflare/src/request.ts index 1ad2e1a8eb91..71f9a52bb786 100644 --- a/packages/cloudflare/src/request.ts +++ b/packages/cloudflare/src/request.ts @@ -18,6 +18,12 @@ interface RequestHandlerWrapperOptions { options: CloudflareOptions; request: Request>; context: ExecutionContext; + /** + * If true, errors will be captured, rethrown and sent to Sentry. + * Otherwise, errors are rethrown but not captured. + * @default true + */ + captureErrors?: boolean; } /** @@ -28,7 +34,7 @@ export function wrapRequestHandler( handler: (...args: unknown[]) => Response | Promise, ): Promise { return withIsolationScope(async isolationScope => { - const { options, request } = wrapperOptions; + const { options, request, captureErrors = true } = wrapperOptions; // In certain situations, the passed context can become undefined. // For example, for Astro while prerendering pages at build time. @@ -67,7 +73,9 @@ export function wrapRequestHandler( try { return await handler(); } catch (e) { - captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); + if (captureErrors) { + captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); + } throw e; } finally { waitUntil?.(flush(2000)); @@ -91,7 +99,9 @@ export function wrapRequestHandler( setHttpStatus(span, res.status); return res; } catch (e) { - captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); + if (captureErrors) { + captureException(e, { mechanism: { handled: false, type: 'cloudflare' } }); + } throw e; } finally { waitUntil?.(flush(2000)); diff --git a/packages/cloudflare/test/request.test.ts b/packages/cloudflare/test/request.test.ts index 32bc8068ba6d..eb2989437396 100644 --- a/packages/cloudflare/test/request.test.ts +++ b/packages/cloudflare/test/request.test.ts @@ -212,6 +212,33 @@ describe('withSentry', () => { expect(thrownError).toBe(error); }); + + test("doesn't capture errors if `captureErrors` is false", async () => { + const captureExceptionSpy = vi.spyOn(SentryCore, 'captureException'); + const error = new Error('test'); + + expect(captureExceptionSpy).not.toHaveBeenCalled(); + let thrownError: Error | undefined; + + try { + await wrapRequestHandler( + { + options: MOCK_OPTIONS, + request: new Request('https://example.com'), + context: createMockExecutionContext(), + captureErrors: false, + }, + () => { + throw error; + }, + ); + } catch (e: any) { + thrownError = e; + } + + expect(captureExceptionSpy).not.toHaveBeenCalled(); + expect(thrownError).toBe(error); + }); }); describe('tracing instrumentation', () => { From 77c3ddf7930602f1a6aa0e8fe0a5ac213db43713 Mon Sep 17 00:00:00 2001 From: Lukas Stracke Date: Wed, 9 Jul 2025 10:36:13 +0200 Subject: [PATCH 2/2] more jsdoc --- packages/cloudflare/src/request.ts | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/cloudflare/src/request.ts b/packages/cloudflare/src/request.ts index 71f9a52bb786..edc1bccef96f 100644 --- a/packages/cloudflare/src/request.ts +++ b/packages/cloudflare/src/request.ts @@ -21,6 +21,11 @@ interface RequestHandlerWrapperOptions { /** * If true, errors will be captured, rethrown and sent to Sentry. * Otherwise, errors are rethrown but not captured. + * + * You most likely don't want to set this to `false`, if you use `wrapRequestHandler` directly. + * This is primarily meant as an escape hatch for higher-level SDKs relying on additional error + * capturing mechanisms where this wrapper captures errors too early or too generally. + * * @default true */ captureErrors?: boolean;