Skip to content

TypeError: value is not a function on SSR when nested {#snippet} reads {const x = $derived(await …)} from parent snippet body via a sync $derived #18469

Description

@hanszoons

Describe the bug

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:

  1. Is this a bug, or by design (i.e. an async $derived in a snippet body can never be closed over by a nested snippet's sync $derived at SSR)?
  2. If by design — what's the idiomatic way to share an awaited value with nested snippets?

Reproduction

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.

Workaround

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()}

Logs

[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 Info

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

Severity

annoyance

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions