Describe the bug
$derived returns wrong value in $effect.pre and attachment cleanup during unmount.
When a component unmount is triggered by state written inside an $effect (a common host→widget sync pattern), reading a $derived during teardown returns the post-change value instead of the pre-change value. This breaks the teardown contract established by #15469 and is the root cause of #15601.
In practice, this means $derived(data.label) inside a teardown throws TypeError: Cannot read properties of undefined when the {#if} guard that protects the render path doesn't protect teardown reads.
The behavior is also inconsistent across teardown types — the same $derived read returns different values depending on where the cleanup runs:
| Teardown position |
Value seen |
$effect.pre cleanup |
post-change (wrong) |
{@attach} cleanup |
post-change (wrong) |
$effect cleanup |
pre-change (correct) |
onDestroy |
pre-change (correct) |
Without the $effect wrapper (setting state directly), all four correctly see the pre-change value.
Conditions required:
- The state change that triggers the unmount happens inside an
$effect
- The
$derived values are read in the template (so they have a cached pre-change value)
Root cause (verified on instrumented 5.51.2): when the write happens inside an effect, the dependent {#if} block is processed through the eager path in flush_queued_effects, which calls old_values.clear() before the destroy teardowns run. Suppressing that clear() restores correct pre-change values for all four positions.
Related: #15601 (this is its root cause), #15469, #14025.
Reproduction
https://svelte.dev/playground/3170f9b03bad47228551d7435a8be42c?version=5.56.2
System Info
System:
OS: macOS 26.5.1
CPU: (10) arm64 Apple M4
Memory: 451.00 MB / 16.00 GB
Shell: 5.9 - /bin/zsh
Binaries:
Node: 24.15.0 - /Users/kiv/.nvm/versions/node/v24.15.0/bin/node
npm: 11.12.1 - /Users/kiv/.nvm/versions/node/v24.15.0/bin/npm
pnpm: 10.33.3 - /Users/kiv/Library/pnpm/pnpm
Browsers:
Chrome: 148.0.7778.179
Firefox: 149.0
Safari: 26.5
npmPackages:
rollup: ^4.57.1 => 4.57.1
svelte: ^5.56.2 => 5.56.2
Severity
annoyance
Describe the bug
$derivedreturns wrong value in$effect.preand attachment cleanup during unmount.When a component unmount is triggered by state written inside an
$effect(a common host→widget sync pattern), reading a$derivedduring teardown returns the post-change value instead of the pre-change value. This breaks the teardown contract established by #15469 and is the root cause of #15601.In practice, this means
$derived(data.label)inside a teardown throwsTypeError: Cannot read properties of undefinedwhen the{#if}guard that protects the render path doesn't protect teardown reads.The behavior is also inconsistent across teardown types — the same
$derivedread returns different values depending on where the cleanup runs:$effect.precleanup{@attach}cleanup$effectcleanuponDestroyWithout the
$effectwrapper (setting state directly), all four correctly see the pre-change value.Conditions required:
$effect$derivedvalues are read in the template (so they have a cached pre-change value)Root cause (verified on instrumented 5.51.2): when the write happens inside an effect, the dependent
{#if}block is processed through the eager path inflush_queued_effects, which callsold_values.clear()before the destroy teardowns run. Suppressing thatclear()restores correct pre-change values for all four positions.Related: #15601 (this is its root cause), #15469, #14025.
Reproduction
https://svelte.dev/playground/3170f9b03bad47228551d7435a8be42c?version=5.56.2
System Info
Severity
annoyance