Svelte

$state mutation inside getter read during flush deadlocks scheduler (regression in 5.53.8)

#17891

Development PRs

Issue

Solved
V
vdg
Mar 10, 2026, 10:01 AM

Describe the bug

A store getter that lazily writes $state on first read deadlocks the scheduler when read inside a template binding during the flush/commit phase. The UI freezes permanently — no errors, no stack overflow, no crash.

Works in: 5.53.7
Broken in: 5.53.8, 5.53.9
Likely cause: "simplify scheduling logic"

Reproduction

https://github.com/vdg/svelte-5.53.8-scheduler-deadlock

git clone https://github.com/vdg/svelte-5.53.8-scheduler-deadlock
cd svelte-5.53.8-scheduler-deadlock
npm install
npm run dev
  1. Click "Open panel"
  2. Click "Counter" — counter does not update, UI is frozen
  3. No console errors

Change to [email protected] → works fine.

The pattern

A module store with a getter that writes $state on first read:

let panelWidth = $state(null)

export default {
  get panelWidth () {
    if (panelWidth === null) panelWidth = DEFAULT_WIDTH  // writes $state
    return panelWidth
  }
}

Read by a template binding:

<div style:width={store.panelWidth ? `${store.panelWidth}px` : null}>

What happens

  1. User action triggers a flush
  2. During commit, the style:width binding reads store.panelWidth for the first time
  3. The getter writes $state (lazy init)
  4. In 5.53.8+, this stalls the scheduler — no further updates are processed

Workaround

Avoid writing $state inside getters:

get panelWidth () {
  return panelWidth ?? DEFAULT_WIDTH  // no state write
}

Severity

Silent — no errors or warnings. The page appears frozen but JS is still running. Very hard to diagnose without knowing to look for this specific pattern.

Info

Closed at Mar 10, 2026, 5:52 PM
Assignees None
Labels None
Milestone None

Pro tip: You can prefix GitHub URLs of issues, PRs or discussions with svcl.dev/ to view them on this page! Also try it on a GitHub release ;)