Skip to content

Commit eef042d

Browse files
committed
Docs: capture the Svelte effect poisoning failure mode in ipc/CLAUDE.md
The original Gotcha covered the cosmetic symptom (literal 'null' in the UI) but not the silent-failure variant that cost most of the F8 debugging session: when a bad `=== undefined` check sits inside a Svelte 5 `$effect`/`$derived` and the downstream code throws on `null`, the throw corrupts the reactive graph for sibling effects on the same component. State writes happen but dependent effects stop running, and any `{#if state}` block stays stuck. Documents the symptom signature and the typical suspect pattern (typed numeric/Intl helpers receiving an optional field) so the next agent can recognize it in <30s instead of <a day.
1 parent 6074cd2 commit eef042d

1 file changed

Lines changed: 12 additions & 0 deletions

File tree

apps/desktop/src/lib/ipc/CLAUDE.md

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -136,6 +136,18 @@ render path) accepts `null` as a real value and surfaces literal `"null"` in the
136136
`dc5f0b47`) flipped the shape across the board; size and date columns rendered the literal text `"null"` until consumers
137137
were switched to `!= null` checks.
138138

139+
**The non-obvious failure mode (lost a day to it):** when the bad `=== undefined` check is inside a Svelte 5 `$effect`
140+
or `$derived` and the downstream code throws on `null` (`formatDateTimeParts(null)`, `null.toLocaleString()`,
141+
`Number(null) + something` ending up `NaN`-poisoning a guard, etc.), the throw doesn't surface as an error in the UI
142+
or the console — it silently corrupts the reactive graph for *sibling* effects on the same component. The classic
143+
signature: a `$state` write happens (you can see the new value if you read the variable synchronously after assigning)
144+
but a separate `$effect` that should re-run on that change *doesn't* re-run, and any `{#if state}` block that depends
145+
on it stays stuck on its previous truth value. We hit this on F8 (delete dialog) after a volume switch: the listing
146+
effect threw on a `null` `modifiedAt` from an SMB/MTP entry, poisoned the graph, and then `showDeleteDialog = true`
147+
silently failed to mount the dialog. The fix is always the same — find the `=== undefined` site that throws on `null`
148+
and switch it to `== null`. Suspect every site that calls a typed function (`Intl.*`, `(n: number) => …`) with an
149+
optional field as input.
150+
139151
## Architecture
140152

141153
The Rust side splits into:

0 commit comments

Comments
 (0)