Skip to content

forkPreloads: after a preloaded navigation to the same route commits, a top-level await keeps the old URL's result #17438

Open

Description

@xyrolle

Describe the bug

With experimental.forkPreloads, a navigation to the same route with different search params shows the new URL, but the page keeps the data it showed for the old URL.

The page reads the URL and awaits a remote query at the top level of its script:

const date = $derived(page.url.searchParams.get('date') ?? '2026-01-05');
const week = $derived(await getWeek(date));
const next = $derived(`?date=${addDays(date, 7)}`);

Hovering the "Next" link preloads ?date=2026-01-12 in a fork, and getWeek('2026-01-12') is requested. Clicking the link commits that navigation: the address bar and page.url.search show ?date=2026-01-12, but week still holds the result for 2026-01-05, so the page shows the old week until the next navigation. No error is logged.

If the navigation is made with goto(next) from a button instead of the link, next keeps its old value too (?date=2026-01-12, the URL we are already on), even though the template's page.url.search is new. So the "Next" button has to be pressed twice to move one week.

With forkPreloads off, the same steps show the new week every time.

Reproduction

https://github.com/xyrolle/sveltekit-fork-preloads-same-route at 51bcdec (pnpm install --frozen-lockfile, pnpm exec playwright install chromium, pnpm build, pnpm probe; versions pinned). The relevant files:

// vite.config.ts: `pnpm build` builds build-on (forkPreloads on) and build-off (FORK=0)
import adapter from '@sveltejs/adapter-node';
import { sveltekit } from '@sveltejs/kit/vite';
import { defineConfig } from 'vite';

const forkPreloads = process.env.FORK !== '0';

export default defineConfig({
	plugins: [
		sveltekit({
			adapter: adapter({ out: forkPreloads ? 'build-on' : 'build-off' }),
			compilerOptions: { experimental: { async: true } },
			experimental: { remoteFunctions: true, forkPreloads }
		})
	]
});
<!-- src/app.html: the body has data-sveltekit-preload-data="hover" -->
<!doctype html>
<html lang="en">
	<head>
		<meta charset="utf-8" />
		%sveltekit.head%
	</head>
	<body data-sveltekit-preload-data="hover">
		<div>%sveltekit.body%</div>
	</body>
</html>
// src/routes/week/data.remote.ts
import { query } from '$app/server';

export const getWeek = query('unchecked', async (date: string) => {
	await new Promise((resolve) => setTimeout(resolve, 300));
	return { date };
});
<!-- src/routes/week/+page.svelte -->
<script lang="ts">
	import { goto } from '$app/navigation';
	import { page } from '$app/state';
	import { getWeek } from './data.remote.ts';

	const addDays = (date: string, days: number) =>
		new Date(Date.parse(`${date}T00:00:00Z`) + days * 86_400_000).toISOString().slice(0, 10);

	const date = $derived(page.url.searchParams.get('date') ?? '2026-01-05');
	const week = $derived(await getWeek(date));
	const next = $derived(`?date=${addDays(date, 7)}`);
</script>

<h1>{week.date}</h1>
<p>{page.url.search}</p>
<a href={next}>Next</a>
<button onclick={() => goto(next)}>Next (goto)</button>

Steps: pnpm build, then node build-on, open http://localhost:3000/week?date=2026-01-05, rest the pointer on "Next" for a second, then click it (or click "Next (goto)"). For comparison, node build-off serves the same app with forkPreloads off.

Expected: the heading shows 2026-01-12, and "Next" links to ?date=2026-01-19.

Actual: the address bar and the <p> show ?date=2026-01-12, the heading still shows 2026-01-05. After "Next (goto)", "Next" also still links to ?date=2026-01-12.

pnpm probe (Playwright, headless Chromium, node build-on / node build-off), 10 loads each. It waits until the address bar and the page's page.url.search line both show ?date=2026-01-12, gives the heading two seconds, and counts the heading still showing exactly 2026-01-05:

old week shown "Next" still ?date=2026-01-12
forkPreloads on, link click 10/10 0/10
forkPreloads on, goto 10/10 10/10
forkPreloads off, link click 0/10 0/10
forkPreloads off, goto 0/10 0/10

With Svelte built from main (86eb09b76) it is the same. With Svelte built from sveltejs/svelte#18988 (head 8a1046e42) both rows with forkPreloads on go to 0/5, so the cause may be in how Svelte commits a fork. sveltejs/svelte#18988 has since been merged into async-another-try-pt-2, not main; that branch is part of sveltejs/svelte#18880, which is still open (retest below).

Logs

No errors in the browser console or the server output.

System Info

System:
  OS: macOS 27.2
  CPU: (18) arm64 Apple M5 Pro
Binaries:
  Node: 24.16.0
Browsers:
  Chromium 153.0.8010.12 (Playwright 1.63.0)
npmPackages:
  @sveltejs/adapter-node: 6.0.0
  @sveltejs/kit: 3.0.1
  @sveltejs/vite-plugin-svelte: 7.3.1
  svelte: 5.57.2
  vite: 8.3.4

Severity

annoyance

Additional Information

The hover also requests getWeek('2026-01-12') twice; that part is #15807 / #17370.

#14996 also goes away without forkPreloads, but there page.params is read inside the async function passed to $derived(await ...), after an await, and the fix suggested there is to pass it in as an argument. Here the argument already is: date is a plain $derived of page.url, read before the await.

Activity

  1. svelte-triage-bot commented on Oct 14, 2026

    @svelte-triage-bot
    Contributor

    I reproduced the issue, but could not produce a safe, verified fix.

    What I tried

    • Loaded forkPreloads: after a preloaded navigation to the same route commits, a top-level await keeps the old URL's result #17438 and comments through the github connection; read repository guidance and forkPreloads API documentation.: Confirmed the current report and reproduction. No issue comments or documented limitation excluding same-route query-parameter navigation.
    • Cloned https://github.com/xyrolle/sveltekit-fork-preloads-same-route; ran pnpm install, pnpm exec playwright install chromium, pnpm build, and pnpm probe with original pinned dependencies.: Reproduced exactly: fork-on link retained the old heading 10/10 times; fork-on goto retained both the old heading and Next URL 10/10 times. Fork-off link and goto had neither stale value on all 10 runs. No console errors or other outcomes.
    • Checked a manual link navigation with agent-browser.: This separate delayed manual navigation showed the updated heading; the supplied automated probe consistently reproduced the reported failure.
    • Overrode Kit with file:/workspace/repos/kit/packages/kit at a2ced7d, added server.fs.allow, rebuilt the same app, and ran N=3 pnpm probe.: Current Kit reproduced both fork-on failures on all 3 runs. Fork-off cases had no stale values. Verified installed client.js matched the checkout byte-for-byte.
    • Searched existing PRs and inspected fix: robustify async reactivity system (pt3) svelte#18988 and fix: deduplicate remote query starts during fork preloads #17371.: Svelte #18988 remains OPEN at 8a1046e426eaca62071c52744bacc7f8240e5532 and addresses fork/batch async reactivity. Kit fix: deduplicate remote query starts during fork preloads #17371 addresses the separate duplicate-request problem, not this stale-data report.
    • Exported Svelte #18988 source, installed it as a file dependency in the same reproduction while retaining unchanged current Kit, ran pnpm build and N=5 pnpm probe.: All four scenarios passed: fork-on/off × link/goto each had 0/5 stale headings and 0/5 stale Next URLs, with no errors or other outcomes. Verified installed batch.js matched the exported PR source byte-for-byte.
    • Recorded evidence in /workspace/notes.md and checked both monorepo working trees.: Both working trees remain clean. No product patch, regression test, commit, or push was made; full repository suites were not run.

    Why I stopped
    An existing open PR already fixes the reproduced bug: sveltejs/svelte#18988. Its exact head fixes both reported navigation paths in the supplied app without any Kit product change. Stopping under the open-PR prior-art rule rather than proposing a competing workaround. This is not a non-reproduction: current Kit with released Svelte fails reliably. A Kit integration regression covering both link and goto navigation can follow once the upstream Svelte fix lands.

  2. xyrolle commented on Oct 22, 2026

    @xyrolle
    Author

    Retested the reproduction with Kit 3.0.1 and everything else unchanged, against packages/svelte from two Svelte commits:

    Svelte link click: old week goto: old week goto: "Next" still ?date=2026-01-12
    main (2334f5e) 5/5 5/5 5/5
    sveltejs/svelte#18880 (7fbce7d) 0/5 0/5 0/5

    With forkPreloads off, every count is 0/5 on both, and every run reports other: none; errors: none.

    sveltejs/svelte#18988, which fixed this in the report, has since been merged into sveltejs/svelte#18880 (through sveltejs/svelte#18881), which is still open. With Kit unchanged, the reproduction passes at that PR's head, so I'm not proposing a Kit change. Once a Svelte release passes it, I'm happy to add a Kit test for both the link and goto navigations.

    Commands and output
    git clone https://github.com/sveltejs/svelte.git svelte-source
    git -C svelte-source fetch origin pull/18880/head
    git clone https://github.com/xyrolle/sveltekit-fork-preloads-same-route.git repro
    git -C repro checkout 51bcdec0f6aac8c5c090e33546818846d17140d6
    
    for sha in \
      2334f5ed0bc9628fddd9767a92f7f12c09e50cd5 \
      7fbce7d8719e9b3a2909e32555ff4c1e16f2637a
    do
      mkdir "svelte-$sha"
      git -C svelte-source archive "$sha" packages/svelte |
        tar -x -C "svelte-$sha" --strip-components=2
      cp -R repro "repro-$sha"
      (
        cd "repro-$sha"
        pnpm add -D "svelte@file:../svelte-$sha"
        pnpm exec playwright install chromium
        pnpm build
        N=5 pnpm probe
      )
    done
    2334f5ed0bc9628fddd9767a92f7f12c09e50cd5
    forkPreloads on, link click: old week shown 5/5, "Next" still ?date=2026-01-12 0/5; other: none; errors: none
    forkPreloads on, goto: old week shown 5/5, "Next" still ?date=2026-01-12 5/5; other: none; errors: none
    forkPreloads off, link click: old week shown 0/5, "Next" still ?date=2026-01-12 0/5; other: none; errors: none
    forkPreloads off, goto: old week shown 0/5, "Next" still ?date=2026-01-12 0/5; other: none; errors: none
    
    7fbce7d8719e9b3a2909e32555ff4c1e16f2637a
    forkPreloads on, link click: old week shown 0/5, "Next" still ?date=2026-01-12 0/5; other: none; errors: none
    forkPreloads on, goto: old week shown 0/5, "Next" still ?date=2026-01-12 0/5; other: none; errors: none
    forkPreloads off, link click: old week shown 0/5, "Next" still ?date=2026-01-12 0/5; other: none; errors: none
    forkPreloads off, goto: old week shown 0/5, "Next" still ?date=2026-01-12 0/5; other: none; errors: none
    

    pnpm 11.28.3, Node 24.16.0, Chromium from Playwright 1.63.0, macOS.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions