sveltejs / svelte · Issue No. 18681
Since 5.25, deriveds declared with let can be reassigned; the docs describe the lifetime as
Derived expressions are recalculated when their dependencies change, but you can temporarily override their values by reassigning them
For a .svelte.ts module compiled with client codegen this holds in every context I measured (owned and unowned, flat and chained). Under server codegen the documented lifetime does not exist: after the reassignment the derived is never evaluated again, so the override survives every dependency change, permanently.
// store.svelte.ts — universal code, runs during SSR and on the client
let seed = $state(1);
let d = $derived.by(() => seed * 10);
d; // 10
d = 999; // override
seed = 2; // dependency change
d; // client build: 20 (documented) · server build: 999, forever
That means a shared rune store (the "universal reactivity" use case) has two different override semantics depending on which build executes it — e.g. an optimistic-UI override applied during SSR is never corrected server-side — and nothing in the docs mentions a difference.
The practical sharp edge is that test setups fall into the server world unnoticed. With Vitest, environment: 'node' gives you the SSR transform (server codegen). And the jsdom default without resolve.conditions: ['browser'] produces a third, mixed state: client codegen but the server top-level svelte entry, where override semantics are client-like but flushSync is a silent no-op and $effect.root effects never fire. We first noticed all this as the same test sequence yielding contradictory results in two files of one repo.
Related, but not the same: #15934 / #15414 cover unowned per-read re-evaluation in the client build — that behaviour does not break the documented override lifetime; the server-codegen divergence reported here is a separate, undocumented one.
Question: is the server behaviour intended (i.e. override lifetime is only specified for the client build)? If so, could the docs state that overrides are permanent under SSR/server codegen — and should reassigning a derived in server code perhaps warn in DEV?
Repository-free repro (a REPL cannot show this — the playground always runs mounted client code, which is exactly the one world that matches the docs):
https://github.com/urbicon/svelte-derived-override-server-codegen — a minimal vitest project with three projects (node env → server codegen; jsdom default → client codegen + server entry; jsdom with resolve.conditions: ['browser'] → full client). bun install && bunx vitest run prints the matrix; the README carries the full result table and the ~20-line scenario module.
| context | codegen | override after dependency change | derived eval count |
|---|---|---|---|
| node env, flat | server | 999 — survives forever | 1 (never re-evaluated after override) |
| node env, chained | server | survives (through the chain) | inner 1 / outer 4 |
| jsdom default, flat/chained/owned | client (+ server entry) | 20 / 21 — documented | 2 |
| jsdom + browser condition, flat/chained/owned | client | 20 / 21 — documented | 2 |
No errors or warnings are emitted in any of the cases — the divergence is silent.
annoyance
Relay reads this issue against the repository's contribution signals: the files it is likely to touch, how the maintainers triage work this size, and what the first contribution would exercise.
The full analysis for this issue is still being assembled. Until then, the description above and the thread on GitHub are the most reliable context.