diff --git a/packages/svelte/src/internal/client/reactivity/sources.js b/packages/svelte/src/internal/client/reactivity/sources.js
index f2ed02a23e..c39184427f 100644
--- a/packages/svelte/src/internal/client/reactivity/sources.js
+++ b/packages/svelte/src/internal/client/reactivity/sources.js
@@ -37,7 +37,8 @@ import {
eager_block_effects,
schedule_effect,
legacy_updates,
- current_batch
+ current_batch,
+ first_batch
} from './batch.js';
import { proxy } from '../proxy.js';
import { execute_derived } from './deriveds.js';
@@ -206,7 +207,11 @@ export function internal_set(source, value, updated_during_traversal = null) {
// During time traveling we don't want to reset the status so that
// traversal of the graph in the other batches still happens
- if (batch_values === null) {
+ if (
+ batch_values === null &&
+ // could also be "read outside of reactivity", e.g. in an event handler
+ !first_batch?.next
+ ) {
update_derived_status(derived);
}
}
diff --git a/packages/svelte/tests/runtime-runes/samples/async-derived-assign-keeps-maybe-dirty/_config.js b/packages/svelte/tests/runtime-runes/samples/async-derived-assign-keeps-maybe-dirty/_config.js
new file mode 100644
index 0000000000..ae9ef996f6
--- /dev/null
+++ b/packages/svelte/tests/runtime-runes/samples/async-derived-assign-keeps-maybe-dirty/_config.js
@@ -0,0 +1,39 @@
+import { tick } from 'svelte';
+import { test } from '../../test';
+
+const buttons =
+ '';
+
+export default test({
+ mode: ['client'],
+ async test({ assert, target, logs }) {
+ await tick();
+ const [s, u, d, resolve_a, resolve_b, resolve_c] = target.querySelectorAll('button');
+
+ // batch A: `d` becomes 10, but is not evaluated yet (the `` is deferred)
+ s.click();
+ await tick();
+
+ // batch B
+ u.click();
+ await tick();
+
+ // batch C overrides `d` while A and B are pending. This must not mark `d` as
+ // clean, because the other batches haven't seen its new value (10) yet
+ d.click();
+ await tick();
+
+ // B sees A's change (but not C's), so `d` is 10 in its world
+ resolve_b.click();
+ await tick();
+ assert.equal(logs[0], 'probe 10');
+
+ resolve_a.click();
+ await tick();
+ assert.htmlEqual(target.innerHTML, `${buttons}