From eb10a70cf1f003c3e93f3c4ecc9749870e7581c6 Mon Sep 17 00:00:00 2001
From: Simon H <5968653+dummdidumm@users.noreply.github.com>
Date: Mon, 18 May 2026 17:36:17 +0200
Subject: [PATCH] fix: unset context synchronously in `run` (#18236)
Our `run` function which executes top level awaits (and synchronous
statements in-between/after) did not unset the context in time in case
the function returns an async value. In that case the context was still
around until the that promise resolves, which can be too late because
unrelated things can be intertwined with the batch.
The test shows this: Without the fix, the unrelated count incrementation
would not update the view until the top level awaits in the child are
done. In the test this just shows as a delayed visual update, but it
also can result in stale roots as shown in
https://github.com/sveltejs/svelte/issues/18221#issuecomment-4470921077
---
.changeset/tangy-icons-dig.md | 5 ++++
.../src/internal/client/reactivity/async.js | 23 ++++++++++------
.../async-run-isolated-batch/Child.svelte | 7 +++++
.../async-run-isolated-batch/_config.js | 26 +++++++++++++++++++
.../async-run-isolated-batch/main.svelte | 23 ++++++++++++++++
5 files changed, 76 insertions(+), 8 deletions(-)
create mode 100644 .changeset/tangy-icons-dig.md
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/Child.svelte
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/_config.js
create mode 100644 packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/main.svelte
diff --git a/.changeset/tangy-icons-dig.md b/.changeset/tangy-icons-dig.md
new file mode 100644
index 0000000000..6d87912bbb
--- /dev/null
+++ b/.changeset/tangy-icons-dig.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: unset context synchronously in `run`
diff --git a/packages/svelte/src/internal/client/reactivity/async.js b/packages/svelte/src/internal/client/reactivity/async.js
index c1d4cbcd67..12c5e9baa5 100644
--- a/packages/svelte/src/internal/client/reactivity/async.js
+++ b/packages/svelte/src/internal/client/reactivity/async.js
@@ -303,15 +303,21 @@ export function run(thunks) {
.then(() => {
restore();
- if (errored) {
- throw errored.error;
+ try {
+ if (errored) {
+ throw errored.error;
+ }
+
+ if (aborted(active)) {
+ throw STALE_REACTION;
+ }
+
+ return fn();
+ } finally {
+ // We gotta unset context directly in case the function returns a promise, in which case
+ // unset_context in .finally() would be too late ...
+ unset_context();
}
-
- if (aborted(active)) {
- throw STALE_REACTION;
- }
-
- return fn();
})
.catch(handle_error);
@@ -320,6 +326,7 @@ export function run(thunks) {
promise.finally(() => {
blocker.settled = true;
+ // ... but we also need it after such a promise has resolved in case it restores our context
unset_context();
});
}
diff --git a/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/Child.svelte b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/Child.svelte
new file mode 100644
index 0000000000..6f84dc319e
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/Child.svelte
@@ -0,0 +1,7 @@
+
+
+{x} {y}
diff --git a/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/_config.js b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/_config.js
new file mode 100644
index 0000000000..a0dc38c660
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/_config.js
@@ -0,0 +1,26 @@
+import { tick } from 'svelte';
+import { test } from '../../test';
+
+export default test({
+ async test({ assert, target }) {
+ const [show, resolve, count] = target.querySelectorAll('button');
+
+ show.click();
+ await tick();
+ resolve.click();
+ await tick();
+ count.click();
+ await tick();
+ assert.htmlEqual(
+ target.innerHTML,
+ ' '
+ );
+
+ resolve.click();
+ await tick();
+ assert.htmlEqual(
+ target.innerHTML,
+ '1 2 '
+ );
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/main.svelte b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/main.svelte
new file mode 100644
index 0000000000..0a5346cc47
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-run-isolated-batch/main.svelte
@@ -0,0 +1,23 @@
+
+
+
+
+{#if show}
+
+{/if}
+
+
+
+