From 16511dd5e1fc4d294e92b8aaf6b815a2f4b491af Mon Sep 17 00:00:00 2001 From: Simon Holthausen Date: Tue, 26 May 2026 21:13:46 +0200 Subject: [PATCH] fix: prevent corrupt batch link state It should be theoretically possible to end up in a state where a batch is unlinked, then relinked (via the `if (this.#roots.length > 0)` logic at the end of `#process`), at which point the links would be wrong if we're not nulling out prev/next during unlink. This adds that, though no test because I wasn't able to come up with one. --- .changeset/khaki-states-train.md | 5 +++++ .../svelte/src/internal/client/reactivity/batch.js | 10 +++++++++- 2 files changed, 14 insertions(+), 1 deletion(-) create mode 100644 .changeset/khaki-states-train.md diff --git a/.changeset/khaki-states-train.md b/.changeset/khaki-states-train.md new file mode 100644 index 0000000000..5d4054fb15 --- /dev/null +++ b/.changeset/khaki-states-train.md @@ -0,0 +1,5 @@ +--- +'svelte': patch +--- + +fix: properly unlink batches diff --git a/packages/svelte/src/internal/client/reactivity/batch.js b/packages/svelte/src/internal/client/reactivity/batch.js index 82c97cf95c..89a1bbf162 100644 --- a/packages/svelte/src/internal/client/reactivity/batch.js +++ b/packages/svelte/src/internal/client/reactivity/batch.js @@ -101,7 +101,7 @@ export class Batch { /** True as soon as `#process` was called */ #started = false; - linked = true; + linked = false; /** @type {Batch | null} */ #prev = null; @@ -975,6 +975,8 @@ export class Batch { } #link() { + if (this.linked) return; + if (last_batch === null) { first_batch = last_batch = this; } else { @@ -983,9 +985,12 @@ export class Batch { } last_batch = this; + this.linked = true; } #unlink() { + if (!this.linked) return; + var prev = this.#prev; var next = this.#next; @@ -1001,6 +1006,9 @@ export class Batch { next.#prev = prev; } + // null out in case it's relinked later + this.#prev = null; + this.#next = null; this.linked = false; } }