sveltejs / svelte · Issue No. 18469
Under compilerOptions.experimental.async, declaring {const value = $derived(await asyncFn())} inside a {#snippet} body and then reading value inside a nested {#snippet} via a synchronous $derived crashes during SvelteKit SSR with TypeError: value is not a function. The client compile is valid and svelte-check stays green; the crash is SSR-only.
My questions:
$derived in a snippet body can never be closed over by a nested snippet's sync $derived at SSR)?Hit / in the preview — 500 immediately.
src/routes/+page.svelte:
<script>
async function getValue() {
return new Set(['a', 'b', 'c']);
}
let override = $state(undefined);
</script>
{#snippet outer()}
{const value = $derived(await getValue())}
{#snippet inner(keys)}
{const all_present = $derived(keys.every((k) => (override ?? value).has(k)))}
<p>all present: {all_present}</p>
{/snippet}
{@render inner(['a', 'b'])}
{@render inner(['a', 'x'])}
{/snippet}
{@render outer()}
svelte.config.js:
import adapter from '@sveltejs/adapter-auto';
const config = {
kit: { adapter: adapter() },
compilerOptions: {
experimental: { async: true },
},
};
export default config;
Note: reading value directly in inner (e.g. <p>{value.has('a')}</p>) does not crash — the compiler wraps that read in $$renderer.async. The crash only triggers when value is read through a nested sync $derived.
Pass await fn() as a snippet parameter; nested snippets then read it via the parameter closure, which works fine on SSR:
{#snippet outer()}
{#snippet inner(value, keys)}
{const all_present = $derived(keys.every((k) => (override ?? value).has(k)))}
<p>all present: {all_present}</p>
{/snippet}
{@render inner(await getValue(), ['a', 'b'])}
{@render inner(await getValue(), ['a', 'x'])}
{/snippet}
{@render outer()}
[500] GET /
TypeError: value is not a function
at src/routes/+page.svelte:10:9
at Array.every (<anonymous>)
at src/routes/+page.svelte:12:38
at .../svelte/src/internal/server/index.js:445:12
at .../svelte/src/internal/server/index.js:498:28
at inner (src/routes/+page.svelte:12:10)
at outer (src/routes/+page.svelte:15:11)
`
Line 10 = {const all_present = $derived(keys.every(...))}, line 12 = <p>all present: {all_present}</p>.
System: macOS 15.7.7, Apple M1 Pro (arm64)
Binaries: Node 24.17.0, pnpm 11.8.0
Packages: svelte 5.56.4, @sveltejs/kit 2.68.0, vite 8.1.0
annoyance
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.