diff --git a/.changeset/khaki-feet-teach.md b/.changeset/khaki-feet-teach.md
new file mode 100644
index 0000000000..51b724428f
--- /dev/null
+++ b/.changeset/khaki-feet-teach.md
@@ -0,0 +1,5 @@
+---
+'svelte': patch
+---
+
+fix: wrap `abort` in `without_reactive_context`
diff --git a/packages/svelte/src/internal/client/reactivity/effects.js b/packages/svelte/src/internal/client/reactivity/effects.js
index f44efa32f1..68a1555032 100644
--- a/packages/svelte/src/internal/client/reactivity/effects.js
+++ b/packages/svelte/src/internal/client/reactivity/effects.js
@@ -42,6 +42,7 @@ import { get_next_sibling } from '../dom/operations.js';
import { component_context, dev_current_component_function, dev_stack } from '../context.js';
import { Batch, schedule_effect } from './batch.js';
import { flatten } from './async.js';
+import { without_reactive_context } from '../dom/elements/bindings/shared.js';
/**
* @param {'$effect' | '$effect.pre' | '$inspect'} rune
@@ -406,7 +407,13 @@ export function destroy_effect_children(signal, remove_dom = false) {
signal.first = signal.last = null;
while (effect !== null) {
- effect.ac?.abort(STALE_REACTION);
+ const controller = effect.ac;
+
+ if (controller !== null) {
+ without_reactive_context(() => {
+ controller.abort(STALE_REACTION);
+ });
+ }
var next = effect.next;
diff --git a/packages/svelte/src/internal/client/runtime.js b/packages/svelte/src/internal/client/runtime.js
index 3d760e4b9e..22a1890e0f 100644
--- a/packages/svelte/src/internal/client/runtime.js
+++ b/packages/svelte/src/internal/client/runtime.js
@@ -46,6 +46,7 @@ import { Batch, batch_deriveds, flushSync, schedule_effect } from './reactivity/
import { handle_error } from './error-handling.js';
import { UNINITIALIZED } from '../../constants.js';
import { captured_signals } from './legacy.js';
+import { without_reactive_context } from './dom/elements/bindings/shared.js';
export let is_updating_effect = false;
@@ -278,7 +279,10 @@ export function update_reaction(reaction) {
update_version = ++read_version;
if (reaction.ac !== null) {
- reaction.ac.abort(STALE_REACTION);
+ without_reactive_context(() => {
+ /** @type {AbortController} */ (reaction.ac).abort(STALE_REACTION);
+ });
+
reaction.ac = null;
}
diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js
new file mode 100644
index 0000000000..2dacf188d7
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/_config.js
@@ -0,0 +1,12 @@
+import { ok, test } from '../../test';
+import { flushSync } from 'svelte';
+
+export default test({
+ async test({ assert, target, errors }) {
+ const btn = target.querySelector('button');
+ flushSync(() => {
+ btn?.click();
+ });
+ assert.deepEqual(errors, []);
+ }
+});
diff --git a/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte
new file mode 100644
index 0000000000..ebefe38fb2
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/abort-signal-derived-set-state/main.svelte
@@ -0,0 +1,24 @@
+
+
+{der}
+
+
\ No newline at end of file