diff --git a/packages/sveltekit/src/server-common/handleError.ts b/packages/sveltekit/src/server-common/handleError.ts index 4e7a1f29ecd5..1586a9bc201e 100644 --- a/packages/sveltekit/src/server-common/handleError.ts +++ b/packages/sveltekit/src/server-common/handleError.ts @@ -1,4 +1,4 @@ -import { captureException, consoleSandbox } from '@sentry/core'; +import { captureException, consoleSandbox, flush } from '@sentry/core'; import type { HandleServerError } from '@sveltejs/kit'; import { flushIfServerless } from '../server-common/utils'; @@ -42,7 +42,23 @@ export function handleErrorWithSentry(handleError: HandleServerError = defaultEr }, }); - await flushIfServerless(); + const platform = input.event.platform as { + context?: { + waitUntil?: (p: Promise) => void; + }; + }; + + // Cloudflare workers have a `waitUntil` method that we can use to flush the event queue + // We already call this in `wrapRequestHandler` from `sentryHandleInitCloudflare` + // However, `handleError` can be invoked when wrapRequestHandler already finished + // (e.g. when responses are streamed / returning promises from load functions) + const cloudflareWaitUntil = platform?.context?.waitUntil; + if (typeof cloudflareWaitUntil === 'function') { + const waitUntil = cloudflareWaitUntil.bind(platform.context); + waitUntil(flush(2000)); + } else { + await flushIfServerless(); + } // We're extra cautious with SafeHandleServerErrorInput - this type is not compatible with HandleServerErrorInput // @ts-expect-error - we're still passing the same object, just with a different (backwards-compatible) type diff --git a/packages/sveltekit/test/server-common/handleError.test.ts b/packages/sveltekit/test/server-common/handleError.test.ts index b24c1c78b747..287e8f2c88e8 100644 --- a/packages/sveltekit/test/server-common/handleError.test.ts +++ b/packages/sveltekit/test/server-common/handleError.test.ts @@ -91,5 +91,29 @@ describe('handleError', () => { // Check that the default handler wasn't invoked expect(consoleErrorSpy).toHaveBeenCalledTimes(0); }); + + it('calls waitUntil if available', async () => { + const wrappedHandleError = handleErrorWithSentry(); + const mockError = new Error('test'); + const waitUntilSpy = vi.fn(); + + await wrappedHandleError({ + error: mockError, + event: { + ...requestEvent, + platform: { + context: { + waitUntil: waitUntilSpy, + }, + }, + }, + status: 500, + message: 'Internal Error', + }); + + expect(waitUntilSpy).toHaveBeenCalledTimes(1); + // flush() returns a promise, this is what we expect here + expect(waitUntilSpy).toHaveBeenCalledWith(expect.any(Promise)); + }); }); });