Skip to content

fix: prevent untracked derived reads from retaining dependencies - #18829

Merged
dummdidumm merged 1 commit into
mainfrom
triage/svelte-18827-untracked-derived-leak
Sep 25, 2026
Merged

dummdidumm merged 1 commit into
mainfrom
triage/svelte-18827-untracked-derived-leak

Conversation

@svelte-triage-bot

Copy link
Copy Markdown
Contributor

Fixes #18827.

A disconnected derived re-evaluated through untrack inside an effect could connect descendant deriveds because is_updating_effect was true, even though the active derived reader was not connected. Those descendants registered on long-lived dependencies without gaining a reaction that could later trigger their disconnect cascade.

This changes get() to connect a derived only when its active reader is itself CONNECTED. Effects already carry that flag, and deriveds reached through connected readers receive it before evaluation, so tracked behavior remains intact. The now-unused is_updating_effect state is removed.

The signals regression covers the reported chain and verifies:

  • removing the tracked reader disconnects the chain;
  • an untracked effect reread stays fresh without reconnecting descendants;
  • a later tracked reader reconnects and remains reactive;
  • teardown removes all source reactions.

A patch changeset is included.

Tests and linting

  • pnpm test signals — 105 passed, 2 skipped
  • pnpm test — 7,784 passed, 55 skipped
  • pnpm check
  • pnpm lint
  • pnpm format
  • git diff --check

@pkg-svelte-dev

Copy link
Copy Markdown

Install the latest version of svelte from 21819c6:

pnpm add https://pkg.svelte.dev/svelte/c/21819c68ba1f03b1414ba5015b9a928778cd2074

Open in pkg.svelte.dev: https://pkg.svelte.dev/repos/svelte/pr/18829

Note

This PR is from a fork. A maintainer must approve approve each commit before it can be built and installed.

@changeset-bot

changeset-bot Bot commented Sep 16, 2026

Copy link
Copy Markdown

🦋 Changeset detected

Latest commit: 21819c6

The changes in this PR will be included in the next version bump.

This PR includes changesets to release 1 package
Name Type
svelte Patch

Not sure what this means? Click here to learn what changesets are.

Click here if you're a maintainer who wants to add another changeset to this PR

@dummdidumm
dummdidumm marked this pull request as ready for review September 25, 2026 21:41
@dummdidumm
dummdidumm merged commit f908535 into sveltejs:main Sep 25, 2026
12 of 13 checks passed
@github-actions github-actions Bot mentioned this pull request Sep 25, 2026
dummdidumm pushed a commit that referenced this pull request Oct 6, 2026
This PR was opened by the [Changesets
release](https://github.com/changesets/action) GitHub action. When
you're ready to do a release, you can merge this and the packages will
be published to npm automatically. If you're not ready to do a release
yet, that's fine, whenever you add more changesets to main, this PR will
be updated.


# Releases
## svelte@5.57.2

### Patch Changes

- fix: don't create async deriveds for a component destroyed while
waiting for its top-level `await`
([#18931](#18931))

- fix: prevent untracked derived reads from retaining disconnected
dependencies ([#18829](#18829))

- fix: error if `{:else}` is followed by another `{:else}` or `{:else if
...}` ([#18900](#18900))

- fix: avoid tracking SvelteURLSearchParams during URL updates
([#18791](#18791))

- fix: restore hydration state when custom element attribute updates
throw ([#18467](#18467))

- fix: attach event handlers that are assigned after a top-level `await`
in production ([#18933](#18933))

- fix: flush anything pending before invoking flushSync callback
function ([#18878](#18878))

- fix: preserve location information for `await` wrappers
([#18859](#18859))

- fix: keep a reactive assignment with no dependencies when `migrate`
cannot turn it into a declaration
([#18817](#18817))

- fix: don't hang `migrate` on a declaration that shares a line with its
script tag ([#18816](#18816))

- fix: prevent duplicate subscriptions when reconnecting deriveds
([#18899](#18899))

- fix: recover when an unexpected node replaces a nested boundary
comment during hydration
([#18925](#18925))

- fix: print `{#await}` blocks that only have a pending branch
([#18908](#18908))

- fix: don't warn about a redundant `link` role on `<area>` elements
without an `href`
([#18872](#18872))

- fix: don't throw `state_unsafe_mutation` when the focused element is
removed while `bind:activeElement` is used
([#18921](#18921))

- fix: don't overwrite an unchanged spread `value`, preserving
incomplete number input
([#18864](#18864))

- fix: read batch-local array on each-block commit
([#18879](#18879))

- fix: error at compile time when a declaration in a snippet redeclares
one of its parameters
([#18842](#18842))

- fix: error when using `let:` directives on a component with a
`children` snippet
([#18873](#18873))

- fix: ignore stale boundary reset callbacks
([#18886](#18886))

- fix: handle deferred transitions aborted before initialization
([#18776](#18776))

- fix: prevent hydration mismatch recovery from being intercepted by
error boundaries
([#18841](#18841))

- fix: preserve dynamic element connections during hydration
([#18855](#18855))

Co-authored-by: github-actions[bot] <41898282+github-actions[bot]@users.noreply.github.com>
dadezzz pushed a commit to dadezzz/events-cash-register that referenced this pull request Oct 10, 2026
This PR contains the following updates:

| Package | Change | [Age](https://docs.renovatebot.com/merge-confidence/) | [Confidence](https://docs.renovatebot.com/merge-confidence/) |
|---|---|---|---|
| [svelte](https://svelte.dev) ([source](https://github.com/sveltejs/svelte/tree/HEAD/packages/svelte)) | [`5.57.1` → `5.57.2`](https://renovatebot.com/diffs/npm/svelte/5.57.1/5.57.2) | ![age](https://developer.mend.io/api/mc/badges/age/npm/svelte/5.57.2?slim=true) | ![confidence](https://developer.mend.io/api/mc/badges/confidence/npm/svelte/5.57.1/5.57.2?slim=true) |

---

### Release Notes

<details>
<summary>sveltejs/svelte (svelte)</summary>

### [`v5.57.2`](https://github.com/sveltejs/svelte/blob/HEAD/packages/svelte/CHANGELOG.md#5572)

[Compare Source](https://github.com/sveltejs/svelte/compare/svelte@5.57.1...svelte@5.57.2)

##### Patch Changes

- fix: don't create async deriveds for a component destroyed while waiting for its top-level `await` ([#&#8203;18931](sveltejs/svelte#18931))

- fix: prevent untracked derived reads from retaining disconnected dependencies ([#&#8203;18829](sveltejs/svelte#18829))

- fix: error if `{:else}` is followed by another `{:else}` or `{:else if ...}` ([#&#8203;18900](sveltejs/svelte#18900))

- fix: avoid tracking SvelteURLSearchParams during URL updates ([#&#8203;18791](sveltejs/svelte#18791))

- fix: restore hydration state when custom element attribute updates throw ([#&#8203;18467](sveltejs/svelte#18467))

- fix: attach event handlers that are assigned after a top-level `await` in production ([#&#8203;18933](sveltejs/svelte#18933))

- fix: flush anything pending before invoking flushSync callback function ([#&#8203;18878](sveltejs/svelte#18878))

- fix: preserve location information for `await` wrappers ([#&#8203;18859](sveltejs/svelte#18859))

- fix: keep a reactive assignment with no dependencies when `migrate` cannot turn it into a declaration ([#&#8203;18817](sveltejs/svelte#18817))

- fix: don't hang `migrate` on a declaration that shares a line with its script tag ([#&#8203;18816](sveltejs/svelte#18816))

- fix: prevent duplicate subscriptions when reconnecting deriveds ([#&#8203;18899](sveltejs/svelte#18899))

- fix: recover when an unexpected node replaces a nested boundary comment during hydration ([#&#8203;18925](sveltejs/svelte#18925))

- fix: print `{#await}` blocks that only have a pending branch ([#&#8203;18908](sveltejs/svelte#18908))

- fix: don't warn about a redundant `link` role on `<area>` elements without an `href` ([#&#8203;18872](sveltejs/svelte#18872))

- fix: don't throw `state_unsafe_mutation` when the focused element is removed while `bind:activeElement` is used ([#&#8203;18921](sveltejs/svelte#18921))

- fix: don't overwrite an unchanged spread `value`, preserving incomplete number input ([#&#8203;18864](sveltejs/svelte#18864))

- fix: read batch-local array on each-block commit ([#&#8203;18879](sveltejs/svelte#18879))

- fix: error at compile time when a declaration in a snippet redeclares one of its parameters ([#&#8203;18842](sveltejs/svelte#18842))

- fix: error when using `let:` directives on a component with a `children` snippet ([#&#8203;18873](sveltejs/svelte#18873))

- fix: ignore stale boundary reset callbacks ([#&#8203;18886](sveltejs/svelte#18886))

- fix: handle deferred transitions aborted before initialization ([#&#8203;18776](sveltejs/svelte#18776))

- fix: prevent hydration mismatch recovery from being intercepted by error boundaries ([#&#8203;18841](sveltejs/svelte#18841))

- fix: preserve dynamic element connections during hydration ([#&#8203;18855](sveltejs/svelte#18855))

</details>

---

### Configuration

📅 **Schedule**: (UTC)

- Branch creation
  - At any time (no schedule defined)
- Automerge
  - At any time (no schedule defined)

🚦 **Automerge**: Disabled by config. Please merge this manually once you are satisfied.

♻ **Rebasing**: Whenever PR becomes conflicted, or you tick the rebase/retry checkbox.

🔕 **Ignore**: Close this PR and you won't be reminded about this update again.

---

 - [ ] <!-- rebase-check -->If you want to rebase/retry this PR, check this box

---

This PR has been generated by [Mend Renovate CLI](https://github.com/renovatebot/renovate).
<!--renovate-debug:eyJjcmVhdGVkSW5WZXIiOiI0NC4xNDguNCIsInVwZGF0ZWRJblZlciI6IjQ0LjE0OC40IiwidGFyZ2V0QnJhbmNoIjoibWFpbiIsImxhYmVscyI6W119-->
DaveCumin added a commit to DaveCumin/AnCiR_next that referenced this pull request Oct 19, 2026
…55.10 to 5.57.2)

ROB-20. Each load of the rest-activity example kept the previous session's workflow alive:
+10,050 DOM nodes, +329 listeners, +2.8 MB heap and 16 old .workflow-node trees per A/B
cycle (production build; dev the same). Retainer: a long-lived effect
(paramDiffWatcher.svelte.js) reads core.data, whose subscriber list still held a derived
from a destroyed NonparametricRA component (also LegendAutoLayout.occupancy), retaining the
old WorkflowEditor through its closure. Cause: in Svelte <= 5.55.10 a derived read inside an
unsubscribed derived (e.g. via untrack in an effect) subscribed itself and was never
detached; fixed upstream in 5.57.2 (sveltejs/svelte#18829, runtime diff confirmed). After:
DOM nodes 4,197 and listeners 584 at every load, 0 old nodes over 10 loads.
Guards: tests/e2e/session-load-leak.spec.js (fails on the old build with 64 old nodes) and
untrackedDerivedRetention.guard.svelte.test.js (fails under 5.55.10).

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
shphrd added a commit to temporal-community/temporal-agent-harness that referenced this pull request Oct 19, 2026
A 40-cycle soak that opens and closes the Session Manager leaked about
128 DOM nodes, 2 listeners and 65 KB per cycle. A $derived in the
closed picker was never disconnected from the controller's run state,
so it kept the picker's virtualizer and its detached rows alive.

Svelte 5.57.2 fixes derived teardown (sveltejs/svelte#18829 and
retained virtualizers.
shphrd added a commit to temporal-community/temporal-agent-harness that referenced this pull request Oct 20, 2026
A 40-cycle soak that opens and closes the Session Manager leaked about
128 DOM nodes, 2 listeners and 65 KB per cycle. A $derived in the
closed picker was never disconnected from the controller's run state,
so it kept the picker's virtualizer and its detached rows alive.

Svelte 5.57.2 fixes derived teardown (sveltejs/svelte#18829 and
retained virtualizers.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

A derived read via untrack() inside an effect leaves its child deriveds connected forever (memory leak: unmounted components pinned through page.data)

1 participant