Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/preload-server-error-handling.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@sveltejs/kit': patch
---

fix: do not invoke client `handleError` for errors already handled on the server when the navigation is a preload
27 changes: 13 additions & 14 deletions packages/kit/src/runtime/client/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -1330,14 +1330,7 @@ async function load_route({ id, invalidating, url, params, route, preload }) {
};
}

if (preload && preload_tokens.has(preload)) {
return preload_error({
error: await handle_error(err, { params, url, route: { id: route.id } }),
url,
params,
route
});
}
const is_preload = !!preload && preload_tokens.has(preload);

let status = get_status(err);
/** @type {App.Error} */
Expand All @@ -1351,17 +1344,23 @@ async function load_route({ id, invalidating, url, params, route, preload }) {
} else if (err instanceof HttpError) {
error = err.body;
} else {
// Referenced node could have been removed due to redeploy, check
const updated = await stores.updated.check();
if (updated) {
// Before reloading, try to update the service worker if it exists
await update_service_worker();
return await native_navigation(url);
if (!is_preload) {
// Referenced node could have been removed due to redeploy, check
const updated = await stores.updated.check();
if (updated) {
// Before reloading, try to update the service worker if it exists
await update_service_worker();
return await native_navigation(url);
}
}

error = await handle_error(err, { params, url, route: { id: route.id } });
}

if (is_preload) {
return preload_error({ error, url, params, route });
}

const error_load = await load_nearest_error_page(i, branch, errors);
if (error_load) {
return get_navigation_result_from_branch({
Expand Down
1 change: 1 addition & 0 deletions packages/kit/test/apps/basics/src/global.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ declare global {
pageContext: any;
mounted: number;
fulfil_navigation: (value: any) => void;
handle_error_calls: Array<{ status: number; message: string }>;
promise: Promise<any>;
PUBLIC_DYNAMIC: string;
}
Expand Down
4 changes: 4 additions & 0 deletions packages/kit/test/apps/basics/src/hooks.client.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,10 @@ window.PUBLIC_DYNAMIC = env.PUBLIC_DYNAMIC;

/** @type{import("@sveltejs/kit").HandleClientError} */
export function handleError({ error, event, status, message }) {
(window.handle_error_calls ??= []).push({
status,
message: /** @type {Error} */ (error).message
});
return event.url.pathname.endsWith('404-fallback')
? undefined
: { message: `${/** @type {Error} */ (error).message} (${status} ${message})` };
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
<script>
import { preloadData } from '$app/navigation';

/** @type {Record<string, any> | null} */
let result = null;

async function preload() {
result = await preloadData('/preloading/preload-error/target');
}
</script>

<button on:click={preload}>Preload</button>

{#if result}
<pre id="result">{JSON.stringify(result)}</pre>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
import { error } from '@sveltejs/kit';

export function load() {
error(404, 'Not found');
}
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
<h1>this page never renders successfully</h1>
11 changes: 11 additions & 0 deletions packages/kit/test/apps/basics/test/client.test.js
Original file line number Diff line number Diff line change
Expand Up @@ -409,7 +409,7 @@
expect(read_errors('/no-ssr/ssr-page-config/layout/inherit')).toBe(undefined);
});

test('cannot use browser-only global on page because of ssr config in +page.js', async ({

Check warning on line 412 in packages/kit/test/apps/basics/test/client.test.js

View workflow job for this annotation

GitHub Actions / test-kit (24, ubuntu-latest, chromium, beta)

flaky test: cannot use browser-only global on page because of ssr config in +page.js

retries: 2
page
}) => {
await page.goto('/no-ssr/ssr-page-config/layout/overwrite');
Expand Down Expand Up @@ -958,7 +958,7 @@
expect(responses.length).toEqual(1);
});

test('data-sveltekit-preload-data', async ({ page }) => {

Check warning on line 961 in packages/kit/test/apps/basics/test/client.test.js

View workflow job for this annotation

GitHub Actions / test-kit (24, ubuntu-latest, chromium, beta)

flaky test: data-sveltekit-preload-data

retries: 2
/** @type {string[]} */
const requests = [];
page.on('request', (req) => {
Expand Down Expand Up @@ -1603,7 +1603,7 @@

const expectGoback = makeExpectGoback(testFinishPage, testEntryPage);

test('app.invalidate', async ({ app, page }) => {

Check warning on line 1606 in packages/kit/test/apps/basics/test/client.test.js

View workflow job for this annotation

GitHub Actions / test-kit (20, ubuntu-latest, chromium, current)

flaky test: app.invalidate

retries: 2
await app.invalidate('app:goto');
await expectGoback(page);
});
Expand Down Expand Up @@ -1789,6 +1789,17 @@
});
});

test.describe('Preloading', () => {
test('does not invoke handleError for server load errors during preload', async ({ page }) => {
await page.goto('/preloading/preload-error');
await page.locator('button').click();
await expect(page.locator('#result')).toContainText('"type":"loaded"');

// the server already handled the error; the client hook must not run
expect(await page.evaluate(() => window.handle_error_calls)).toBeUndefined();
});
});

test.describe('reroute', () => {
test('Apply reroute during client side navigation', async ({ page, clicknav }) => {
await page.goto('/reroute/basic');
Expand Down Expand Up @@ -1887,7 +1898,7 @@
});

test.describe('INP', () => {
test('does not block next paint', async ({ page }) => {

Check warning on line 1901 in packages/kit/test/apps/basics/test/client.test.js

View workflow job for this annotation

GitHub Actions / test-kit (20, ubuntu-latest, chromium, current)

flaky test: does not block next paint

retries: 2

Check warning on line 1901 in packages/kit/test/apps/basics/test/client.test.js

View workflow job for this annotation

GitHub Actions / test-kit (18, ubuntu-latest, chromium, baseline)

flaky test: does not block next paint

retries: 2
// Thanks to https://publishing-project.rivendellweb.net/measuring-performance-tasks-with-playwright/#interaction-to-next-paint-inp
/** @param {string} selector */
async function measureInteractionToPaint(selector) {
Expand Down
Loading