Skip to content

tab freezes when an awaited value feeding a chart changes (async svelte + kit remote functions) #895

Description

@teidesu

the bug

if i change the argument of an awaited SvelteKit remote-function query() inside a <svelte:boundary>, that renders a LayerChart chart inside - the tab hangs in what i can only assume to be a main-thread infinite js loop: cpu usage is at 100%, and the page stays frozen indefinitely. initial render (ssr and hydration) is (usually) fine; the freeze happens only when the awaited value re-resolves while the chart subtree is alive.

Filing here because LayerChart's derived graph is what the loop walks, but this might also be a svelte/sveltekit issue

repro

git clone https://github.com/teidesu/layerchart-async-repro && cd layerchart-async-repro
pnpm install
pnpm dev

if the tab didn't freeze right away - click the button once.

config: compilerOptions.experimental.async: true + kit.experimental.remoteFunctions: true.

versions

  • layerchart 2.1.0
  • svelte 5.56.8 (also reproduces on 5.56.4)
  • @sveltejs/kit 2.63
  • vite 8, Chrome 150, macOS
findings by claude fable 5

What I measured

Patched update_derived in svelte's client runtime with a counter: page load is ~13k derived updates, then a single click drives it past 2,000,000 — still climbing when I made the counter throw to free the tab. So it's an unbounded
derived re-evaluation storm, not a slow render.

Pausing the wedged renderer (debugger attached before the click) shows a ~780-frame stack cycling through:

get → update_derived → execute_derived → update_reaction → (layerchart derived) → get → ...

interleaved with nested $state-proxy ownKeys recursion through exclude_from_object (rest-props destructuring) in LayerChart's component chain (restProps = user_derived(() => exclude_from_object(props, [...])) etc. in the Chart component). The graph never settles — deriveds producing fresh object identities keep re-capturing into the async batch/fork during the boundary re-render.

Notes / controls

  • Replacing the remote query with a plain local async function of identical shape (same await in the boundary, same 100ms delay) does not reproduce. The remote-function query object is load-bearing.
  • Fresh mounts are always fine; only a re-resolve over a live chart subtree freezes. {#key data} around the chart does not help (the remount itself storms). motion: 'none' doesn't help either.
  • $effect.pending() is not required (this repro has none), but pages reading it freeze the same way.
  • Looks like a survivor of the async-mode round from last year: sveltejs/svelte#17883 (same symptom, closed), sveltejs/svelte#16548, #626, #630 — all marked fixed, but this variant reproduces on latest svelte.

Activity

  1. techniq commented on Aug 6, 2026

    @techniq
    Owner

    thanks @teidesu, taking a look

  2. techniq commented on Aug 6, 2026

    @techniq
    Owner

    definitely feeling like a Svelte/Kit regression, but not 100% sure yet. I'll ask in Discord but might be best to open an issue as well.

    btw, I also created a fork of your repo, cleaned up a few things and split the route into 2 - async w/ remote functions and sync with client-side data.

    https://github.com/techniq/layerchart-async-repro

  3. techniq commented on Aug 12, 2026

    @techniq
    Owner

    @teidesu fix might be coming soon... sveltejs/svelte#18558 (comment)

  4. half2me commented on Aug 20, 2026

    @half2me

    You may have been chasing a bug that was never in layerchart. I hit the same freeze from an app of my own, and it reduces out of the library entirely.

    The cause is in SvelteKit, in Query.set() (packages/kit/src/runtime/client/remote-functions/query/instance.svelte.js). It unconditionally replaced its internal promise with a fresh resolved one, including when a request was already in flight and #clear_pending() had just resolved the promise that awaiting consumers were suspended on. Replacing it as well re-suspended an already-awaiting consumer in a second batch while the first was still pending. Two live batches put Svelte into "time travelling" mode, where deriveds are deliberately never marked clean, so everything downstream of the query recomputes on every read instead of once, at a cost exponential in the depth of the derived graph.

    Two things that surprised me on the way. It is not an infinite loop: counting inside Svelte's runtime, update_effect stays flat (59 with layerchart, 6 with a reduced fixture) while update_derived climbs into the millions. It is one effect execution doing exponentially much work. And it is not layerchart specific: it reduces to ~45 lines with no dependencies, a derived graph alternating nodes that return a fresh object with nodes that return a constant, so the constant ones stay permanently dirty against the ones above them. Depth 6 costs 14 recomputations when memoization holds and 27,305 when it does not; depth 12 wedges the tab. layerchart simply has a graph deep enough to make it fatal, and is doing nothing unusual.

    Fix is sveltejs/kit#16855 (details and measurements there). With it, my reproduction goes from FROZEN on the first press to 25ms/11bars / 25ms/12bars / 18ms/13bars / 27ms/14bars, redrawing every press. It works on stock svelte@5.56.8, so sveltejs/svelte#18558 is not required for this one, though I have not tested whether it would also fix it.

    One caveat before you close anything: I verified this against layerchart@2.0.2 and my own reproduction, on @sveltejs/kit 3.0.0-next.23, and 2.64.0 is clean. @teidesu's report here is against Kit 2.63, so if that one still freezes it may be a different path from the one #16855 closes, and I am not claiming to have explained it. There is an install link on the PR if it is cheap to check.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions