Skip to content

Commit dc9238b

Browse files
authored
fix(sveltekit): Ensure server errors from streamed responses are sent (#17044)
Make sure we use of Cloudflare's `waitUntil` function which fortunately is available within the error handler hook. This fixes error reporting for streamed server responses on Clourdlfare, where the returned promise rejects after the initial response was sent.
1 parent e8f2b2d commit dc9238b

File tree

2 files changed

+42
-2
lines changed

2 files changed

+42
-2
lines changed

packages/sveltekit/src/server-common/handleError.ts

Lines changed: 18 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { captureException, consoleSandbox } from '@sentry/core';
1+
import { captureException, consoleSandbox, flush } from '@sentry/core';
22
import type { HandleServerError } from '@sveltejs/kit';
33
import { flushIfServerless } from '../server-common/utils';
44

@@ -42,7 +42,23 @@ export function handleErrorWithSentry(handleError: HandleServerError = defaultEr
4242
},
4343
});
4444

45-
await flushIfServerless();
45+
const platform = input.event.platform as {
46+
context?: {
47+
waitUntil?: (p: Promise<void>) => void;
48+
};
49+
};
50+
51+
// Cloudflare workers have a `waitUntil` method that we can use to flush the event queue
52+
// We already call this in `wrapRequestHandler` from `sentryHandleInitCloudflare`
53+
// However, `handleError` can be invoked when wrapRequestHandler already finished
54+
// (e.g. when responses are streamed / returning promises from load functions)
55+
const cloudflareWaitUntil = platform?.context?.waitUntil;
56+
if (typeof cloudflareWaitUntil === 'function') {
57+
const waitUntil = cloudflareWaitUntil.bind(platform.context);
58+
waitUntil(flush(2000));
59+
} else {
60+
await flushIfServerless();
61+
}
4662

4763
// We're extra cautious with SafeHandleServerErrorInput - this type is not compatible with HandleServerErrorInput
4864
// @ts-expect-error - we're still passing the same object, just with a different (backwards-compatible) type

packages/sveltekit/test/server-common/handleError.test.ts

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,5 +91,29 @@ describe('handleError', () => {
9191
// Check that the default handler wasn't invoked
9292
expect(consoleErrorSpy).toHaveBeenCalledTimes(0);
9393
});
94+
95+
it('calls waitUntil if available', async () => {
96+
const wrappedHandleError = handleErrorWithSentry();
97+
const mockError = new Error('test');
98+
const waitUntilSpy = vi.fn();
99+
100+
await wrappedHandleError({
101+
error: mockError,
102+
event: {
103+
...requestEvent,
104+
platform: {
105+
context: {
106+
waitUntil: waitUntilSpy,
107+
},
108+
},
109+
},
110+
status: 500,
111+
message: 'Internal Error',
112+
});
113+
114+
expect(waitUntilSpy).toHaveBeenCalledTimes(1);
115+
// flush() returns a promise, this is what we expect here
116+
expect(waitUntilSpy).toHaveBeenCalledWith(expect.any(Promise));
117+
});
94118
});
95119
});

0 commit comments

Comments
 (0)