Skip to content

Command can read destroyed reactive state from deferred afterTick work #2112

Description

@kolaworld

Describe the bug

Unmounting a conditionally rendered Command can emit Svelte's derived_inert warning:

[svelte] derived_inert
Reading a derived belonging to a now-destroyed effect may result in stale values
https://svelte.dev/e/derived_inert

Closing a nested picker unmounts its Command root and can produce the warning during teardown. The command still closes correctly, so the observed impact is warning noise.

The timing and source suggest that deferred Command work outlives its component. One suspected path starts in registerItem(): its disposer can call #selectFirstItem(), whose afterTick callback calls getValidItems() and reads the root's derived ref after destruction.

Deferred sorting, scrolling, and #scheduleUpdate() callbacks are also not teardown-aware in bits-ui@2.19.0.

This appears to be the same lifecycle-error family as issue #2080, fixed by PR #2087 and the deferred-work findings in issue #2103 and issue #2106.

PR #2107 likewise prevents access to a destroyed boxed ref by checking non-reactive lifecycle state first. None of these changes cover Command.

Every deferred CommandRootState callback should return before reading reactive state after teardown. afterTick work needs a lifecycle guard; cancellable work should also be cancelled during teardown.

Reproduction

https://stackblitz.com/edit/vitejs-vite-ykwxohtm?file=src%2FApp.svelte

Logs

[svelte] derived_inert
Reading a derived belonging to a now-destroyed effect may result in stale values
https://svelte.dev/e/derived_inert

System Info

bits-ui: 2.19.0
svelte: 5.56.5
@sveltejs/kit: 2.69.3
bun: 1.3.14

Severity

annoyance

Metadata

Metadata

Assignees

No one assigned

    Labels

    triageA maintainer needs to review this issue and label it appropriately

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions