fix: defer child-store root disposal to avoid nested cleanNode

disposeDirectory called a createRoot dispose() synchronously. When
triggered by pinForOwner's onCleanup during a parent remount (e.g.
switching to a WSL server re-keys the ServerKey Show), the inner
dispose ran a nested cleanNode cascade on a sibling root while the
outer cascade was mid-traversal, corrupting solid-js's graph walk
state and surfacing as TypeError: Cannot read properties of null
(reading '1') at chunk-*.js:992 after ~155 recursive cleanNode frames.

Queue the dispose on a microtask so synchronous bookkeeping still
runs (map deletes, onDispose cache invalidation) but the reactive
cleanup happens after the outer traversal finishes.
This commit is contained in:
LukeParkerDev
2026-04-17 11:50:49 +10:00
parent cfcc6f1353
commit d04d13ea22

View File

@@ -96,8 +96,15 @@ export function createChildStoreManager(input: {
lifecycle.delete(directory)
const dispose = disposers.get(directory)
if (dispose) {
dispose()
disposers.delete(directory)
// Defer the actual solid-js root disposal. When disposeDirectory runs
// from pinForOwner's onCleanup during a parent remount, calling
// dispose() here triggers a nested cleanNode cascade on the inner
// root while the outer cascade is mid-traversal, which corrupts
// solid-js's graph walk state and throws `Cannot read properties of
// null (reading '1')` at chunk-*.js:992. Running dispose on a
// microtask lets the outer cleanup finish first.
queueMicrotask(dispose)
}
delete children[directory]
input.onDispose(directory)