[5.55.3] derived_inert warnings
#18097
derived_inert warningsDevelopment PRs
A nested $effect.root was marked INERT during pause_children, which caused it to stay in that state indefinetly after the rest of the parent tree was destroyed. Consequently deriveds inside no longer update and cause warnings.
This fixes it by not marking nested $effect.roots as inert, just like nested $effect.roots are not destryoed and instead become a new root.
Fixes #18097
It's possible to rebase just-created batches.
Case A:
- batch A runs effects
- one of these effects writes to a source. This creates a new batch B
- an effect after that (still part of "flush effects of batch A") executes a derived. This creates an entry in the
currentMap in batch B - batch A commits after processing batch B (
next_batchetc logic), batch B is pending. Due to derived being part of batchB.current batch A can wrongfully think these are connected and try to rerun/add effects etc on batch B
Case B:
- like case A but with an additional await inside a pending snippet
Case C:
- batch A with source a and b, it flushes effects
- one of these effects schedules batch B with b and c scheduling an async effect
- batch B is deferred
- batch A commits. Due to the a/b/c partial overlap it will needlessly rerun the just scheduled async effect
All these cases are wrong. We fix it like this:
- we call
this.#commit()before running the new batches, which may stick around due to having pending work, and we don't want to rebase these. This fixes case A and C - we capture derived values in
previous_batchif it exists, because it means we're currently flushing effects, and derived writes belong to that batch and not a new one that might have been scheduled already. This fixes case B
Discovered this while working on #18097
We shouldn't continue executing async work where we know the surrounding branch is destroyed already, it can leave to noisy "derived inter" warnings or even runtime errors ("cannot stringify symbol" when running a template effect with an uninitialized source). Neither should we warn about waterfalls on an already-destroyed async effect.
Fixes #18097 (though strictly speaking that particular instance is also fixed by #18117 which fixes the underlying cause for the reruns; this one is necessary in itself though, as shown by the new test)
Issue
Describe the bug
I got a bunch of this warning when updated Svelte to 5.55.3. This is caused by some weird interactions between a couple of shadcn-svelte, bits-ui, svelte-sonner components and page.url. Created a reproduction as requested by @dummdidumm.
Reproduction
Logs
[svelte] derived_inert Reading a derived belonging to a now-destroyed effect may result in stale values
System Info
[email protected]
[email protected]
Severity
annoyance
Info
I see this as well. This is blocking as it breaks my app (I see stale values).
Reverting to 5.55.2 resolves it.
Can you provide a reproduction where you see stale values? Does using $effect.root or having the derived created outside of an effect as help?
Can you provide a reproduction where you see stale values? Does using
$effect.rootor having the derived created outside of an effect as help?
Hi! I do not have the bandwidth to create a repro atm unfortunately.
I do want to say that the whole error message is nonsensical for me. I am saying thid as someone who have used svelte and sveltekit for years but not have the deep knowledge of the inner workings which seems to be required nowadays.
As for the warning and its explanation:
A $derived value created inside an effect...
Inside an effect? Does effect here mean $effect or something in a wider sense, and if so what? I definitely do not create a $derived in an $effect and I didn't even know it was a thing. Therefore it's hard for me to move it outside.
It would be easier to pin down if the warning pointed at the code causing the warning. But at first glance (I'll check more tomorrow) it seemed to just point at sveltekit remote function internals.
I'm getting stale values when using a SvelteKit remote function. It doesn't happen every time, but whenever the warning appears in the console, from then on, the DOM will no longer update.
My component basically looks like this:
<script>
import * as RemoteGoal from "$fns/goal.remote"
const { goal } = $derived(await RemoteGoal.currentGoal())
</script>
{goal.books_amount}
<GoalUpdateForm />
The GoalUpdateForm calls the remote form RemoteGoal.update, which look like this:
// goal.remote.ts
export const currentGoal = query(async () => {
const goal = await GoalRepo.currentGoal()
return { goal }
})
export const update = form(goalSchema, async (data) => {
await GoalRepo.update(data)
await currentGoal().refresh()
})
I can only reproduce this if I have the component on two different routes. If I open the first route and DON'T interact, then go to the second route and call the remote form, I get the warning in the console and the DOM doesn't update. From then on, the derived is stuck.
Since the warning mentions the derived, I even tried simplifying it so that I don't call $derived at all. But the error is the same:
<script>
import * as RemoteGoal from "$fns/goal.remote"
</script>
{(await RemoteGoal.currentGoal()).goal.books_amount}
<GoalUpdateForm />
(Since this code doesn't contain $derived or $effect at all, I must agree with @einarpersson that the error message is not very helpful in figuring out what is wrong in the code.)
Downgrading to 5.55.2 fixes it for me as well.
It's hard to test for stale values specifically attributed to this warning because there are other bugs introduced in 5.55.3 that break my app entirely. It would require me to rewrite a significant portion of it to work around the bugs (related to @const). Everything was working in 5.55.2 so I'd rather downgrade and wait for a fix.
I don't have any $effect or $effect.root in the code that I own, nor do I see the needs to use them. There could be in libraries that I use, especially bits-ui. At first I thought the bug was caused by @const or something else, but then I couldn't reproduce it in a fresh project. Then I remembered that something similar happened before: an unrelated component in the layout that had $effect in its internal broke the app. Last time it was Avatar, which I replaced. Now it's Toaster from svelte-sonner.
Also, I have fork and preload-data disabled to avoid bugs related to them. Dealing with other async bugs is hard enough. So the warnings I got weren't related to navigation.
I'm getting stale values when using a SvelteKit remote function. It doesn't happen every time, but whenever the warning appears in the console, from then on, the DOM will no longer update.
My component basically looks like this:
<script> import * as RemoteGoal from "$fns/goal.remote" const { goal } = $derived(await RemoteGoal.currentGoal()) </script>{goal.books_amount}
The
GoalUpdateFormcalls the remote formRemoteGoal.update, which look like this:// goal.remote.ts
export const currentGoal = query(async () => { const goal = await GoalRepo.currentGoal() return { goal } })
export const update = form(goalSchema, async (data) => { await GoalRepo.update(data) await currentGoal().refresh() })
I can only reproduce this if I have the component on two different routes. If I open the first route and DON'T interact, then go to the second route and call the remote form, I get the warning in the console and the DOM doesn't update. From then on, the derived is stuck.
Since the warning mentions the
<script> import * as RemoteGoal from "$fns/goal.remote" </script>derived, I even tried simplifying it so that I don't call$derivedat all. But the error is the same:{(await RemoteGoal.currentGoal()).goal.books_amount}
(Since this code doesn't contain
$derivedor$effectat all, I must agree with @einarpersson that the error message is not very helpful in figuring out what is wrong in the code.)Downgrading to 5.55.2 fixes it for me as well.
This resembles my setup and matches the behaviour I am seeing.
should be better now but still seeing it on the original reproduction
With v5.55.4, I'm not able to reproduce my problem with remote functions anymore. Thank you for the quick fix!
I can reproduce same warning every time starting with 5.55.3:
- page A has remote query and
svelte:boundary - this remote query takes too long to finish (e.g. 3 seconds delay)
- before query is finished you go to another page B
- when remote query from page A finishes you get this warning
I'm using version 5.55.5 and still seeing this issue! Its just a normal warning in dev server, but occurs an error in production server!
In my case, it happens because of the immediate variable destroying. My code looks like:
{#key schedule.by}
{@const direction = schedule.by == 'time' ? 1 : -1}
<div in:fly={{ x: 80 * direction }} out:fly={{ x: -80 * direction }}>
...
This is a simple Time/Price switch, I need the key block because of triggering an animation.
In dev server, its just a 'derived_inert' warning. But in production it leads to Uncaught TypeError: can't convert symbol to number. My guess is before the 5.55.3 svelte used to keep the stale variables in the key block, until its fully destroyed. But in newer version, it immediately destroys the variable used by @const and makes it a Symbol.
Its unexpected! Key block supposed to be keeping the slate variables until its destroyed.
Switching back to 5.55.2 fixes it for now. Waiting for a reliable update!