sveltejs / svelte · Issue No. 18675
Under compilerOptions.experimental.async, Svelte throws an uncaught invariant_violation: "Batch has scheduled roots" when a branch created after the initial render (an {#if} toggled later — in our app, opening a dialog) contains all four of:
$effect.pending(){#each await promise as item} blockbind:this inside the each bodyawait expression inside the each bodyRemoving any one of the four makes the error disappear (verified by ablation). An already-resolved Promise.resolve([1]) is enough — no timing race is needed and the repro is deterministic (100% of loads). The UI still renders correctly after the error.
Also observed:
show from a click handler instead of setTimeout reproduces as well.{#if}) does not reproduce.<svelte:boundary> inside the {#if} suppresses the error, but a pre-existing boundary around the {#if} (with a pending snippet) does not.Originally hit in production code where a list component reads $effect.pending() for a debounced loading indicator while rendering {#each await query} rows that contain a component with a bound ref and a nested await — first seen behind SvelteKit + bits-ui + a custom thenable, all of which turned out to be red herrings.
Possibly related: #18363 (same invariant, different trigger).
<script>
let show = $state(false);
setTimeout(() => (show = true), 50);
const docs = Promise.resolve([1]);
let el;
</script>
{#if show}
{$effect.pending()}
{#each await docs as doc}
<button bind:this={el}>
{(await docs).length}
</button>
{/each}
{/if}
Load the page; the error is thrown ~50 ms later when show flips. Reproduces in headless and headed Chromium; also reproduces with plain Vite + @sveltejs/vite-plugin-svelte (no SvelteKit) with compilerOptions: { experimental: { async: true } } as the only non-default flag.
Svelte error: invariant_violation
An invariant violation occurred, meaning Svelte's internal assumptions were flawed. This is a bug in Svelte, not your app — please open an issue at https://github.com/sveltejs/svelte, citing the following message: "Batch has scheduled roots"
https://svelte.dev/e/invariant_violation
at invariant_violation (svelte/src/internal/client/errors.js)
at invariant (svelte/src/internal/client/reactivity/batch.js)
at #commit (Batch)
at #process (Batch)
at Batch.flush
at run_all
at run_micro_tasks
The failing guard is invariant(batch.#roots.length === 0, 'Batch has scheduled roots') in src/internal/client/reactivity/batch.js (#commit).
Reproduced on svelte 5.56.3 and 5.56.9, and on the svelte.dev playground as of 2026-08-19.
vite 8.0.16, @sveltejs/vite-plugin-svelte 7.1.2 and 7.3.0.
Chromium 145 (headless shell and headed), Linux.
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.