From fe9ab936b6aa646ef87c636ec8803d42f8b7bb1d Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Fri, 22 May 2026 17:48:31 +0200 Subject: [PATCH] fix: allow `$derived(await ...)` in disconnected effect roots (#18273) If `$effect.root` is created outside the component tree, the boundary property is null Fixes #18188 --- .changeset/fine-corners-lay.md | 5 +++++ .../svelte/src/internal/client/reactivity/deriveds.js | 5 ++++- .../async-async-disconnected-effect-root/_config.js | 9 +++++++++ .../async-async-disconnected-effect-root/main.svelte | 11 +++++++++++ 4 files changed, 29 insertions(+), 1 deletion(-) create mode 100644 .changeset/fine-corners-lay.md create mode 100644 packages/svelte/tests/runtime-runes/samples/async-async-disconnected-effect-root/_config.js create mode 100644 packages/svelte/tests/runtime-runes/samples/async-async-disconnected-effect-root/main.svelte diff --git a/.changeset/fine-corners-lay.md b/.changeset/fine-corners-lay.md new file mode 100644 index 0000000000..badbc6f81f --- /dev/null +++ b/.changeset/fine-corners-lay.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: allow `$derived(await ...)` in disconnected effect roots diff --git a/packages/svelte/src/internal/client/reactivity/deriveds.js b/packages/svelte/src/internal/client/reactivity/deriveds.js index 8d240bca31..7ea6de6306 100644 --- a/packages/svelte/src/internal/client/reactivity/deriveds.js +++ b/packages/svelte/src/internal/client/reactivity/deriveds.js @@ -187,7 +187,10 @@ export function async_derived(fn, label, location) { var decrement_pending = increment_pending(); } - if (/** @type {Boundary} */ (parent.b).is_rendered()) { + if ( + // boundary can be null if the async derived is inside an $effect.root not connected to the component render tree + parent.b?.is_rendered() + ) { batch.async_deriveds.get(effect)?.reject(OBSOLETE); } else { // While the boundary is still showing pending, a new run supersedes all older in-flight runs diff --git a/packages/svelte/tests/runtime-runes/samples/async-async-disconnected-effect-root/_config.js b/packages/svelte/tests/runtime-runes/samples/async-async-disconnected-effect-root/_config.js new file mode 100644 index 0000000000..ecf048abc3 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-async-disconnected-effect-root/_config.js @@ -0,0 +1,9 @@ +import { test } from '../../test'; + +export default test({ + // Test that an async derived inside an $effect.root not connected to the component tree still works + async test({ assert, logs }) { + await new Promise((resolve) => setTimeout(resolve, 10)); + assert.deepEqual(logs, [1]); + } +}); diff --git a/packages/svelte/tests/runtime-runes/samples/async-async-disconnected-effect-root/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-async-disconnected-effect-root/main.svelte new file mode 100644 index 0000000000..37c8919023 --- /dev/null +++ b/packages/svelte/tests/runtime-runes/samples/async-async-disconnected-effect-root/main.svelte @@ -0,0 +1,11 @@ +