Skip to content

Commit 212846c

Browse files
committed
fix(sveltekit): Avoid capturing "Not Found" errors on client side
1 parent a4e6a66 commit 212846c

File tree

2 files changed

+41
-8
lines changed

2 files changed

+41
-8
lines changed

packages/sveltekit/src/client/handleError.ts

Lines changed: 24 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,23 +11,39 @@ function defaultErrorHandler({ error }: Parameters<HandleClientError>[0]): Retur
1111
});
1212
}
1313

14-
// TODO: add backwards-compatible type for kit 1.x (soon)
1514
type HandleClientErrorInput = Parameters<HandleClientError>[0];
1615

16+
/**
17+
* Backwards-compatible HandleServerError Input type for SvelteKit 1.x and 2.x
18+
* `message` and `status` were added in 2.x.
19+
* For backwards-compatibility, we make them optional
20+
*
21+
* @see https://kit.svelte.dev/docs/migrating-to-sveltekit-2#improved-error-handling
22+
*/
23+
type SafeHandleServerErrorInput = Omit<HandleClientErrorInput, 'status' | 'message'> &
24+
Partial<Pick<HandleClientErrorInput, 'status' | 'message'>>;
25+
1726
/**
1827
* Wrapper for the SvelteKit error handler that sends the error to Sentry.
1928
*
2029
* @param handleError The original SvelteKit error handler.
2130
*/
2231
export function handleErrorWithSentry(handleError: HandleClientError = defaultErrorHandler): HandleClientError {
23-
return (input: HandleClientErrorInput): ReturnType<HandleClientError> => {
24-
captureException(input.error, {
25-
mechanism: {
26-
type: 'sveltekit',
27-
handled: false,
28-
},
29-
});
32+
return (input: SafeHandleServerErrorInput): ReturnType<HandleClientError> => {
33+
const { status, message } = input;
34+
const isNotFoundError = status === 404 && message === 'Not Found';
35+
36+
if (!isNotFoundError) {
37+
captureException(input.error, {
38+
mechanism: {
39+
type: 'sveltekit',
40+
handled: false,
41+
},
42+
});
43+
}
3044

45+
// We're extra cautious with SafeHandleServerErrorInput - this type is not compatible with HandleServerErrorInput
46+
// @ts-expect-error - we're still passing the same object, just with a different (backwards-compatible) type
3147
return handleError(input);
3248
};
3349
}

packages/sveltekit/test/client/handleError.test.ts

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,6 +38,7 @@ describe('handleError', () => {
3838
it('invokes the default handler if no handleError func is provided', async () => {
3939
const wrappedHandleError = handleErrorWithSentry();
4040
const mockError = new Error('test');
41+
// @ts-expect-error - purposefully omitting status and message to cover SvelteKit 1.x compatibility
4142
const returnVal = await wrappedHandleError({ error: mockError, event: navigationEvent });
4243

4344
expect(returnVal).not.toBeDefined();
@@ -50,6 +51,7 @@ describe('handleError', () => {
5051
it('invokes the user-provided error handler', async () => {
5152
const wrappedHandleError = handleErrorWithSentry(handleError);
5253
const mockError = new Error('test');
54+
// @ts-expect-error - purposefully omitting status and message to cover SvelteKit 1.x compatibility
5355
const returnVal = (await wrappedHandleError({ error: mockError, event: navigationEvent })) as any;
5456

5557
expect(returnVal.message).toEqual('Whoops!');
@@ -59,4 +61,19 @@ describe('handleError', () => {
5961
expect(consoleErrorSpy).toHaveBeenCalledTimes(0);
6062
});
6163
});
64+
65+
it('doesn\'t capture "Not Found" errors', async () => {
66+
const wrappedHandleError = handleErrorWithSentry(handleError);
67+
const returnVal = (await wrappedHandleError({
68+
error: new Error('404 Not Found'),
69+
event: navigationEvent,
70+
status: 404,
71+
message: 'Not Found',
72+
})) as any;
73+
74+
expect(returnVal.message).toEqual('Whoops!');
75+
expect(mockCaptureException).not.toHaveBeenCalled();
76+
// Check that the default handler wasn't invoked
77+
expect(consoleErrorSpy).toHaveBeenCalledTimes(0);
78+
});
6279
});

0 commit comments

Comments
 (0)