Skip to content

fix: don't call into a disposed list property when useRiveList unmounts - #408

Open
mfazekas wants to merge 4 commits into
mainfrom
claude/fix-407-use-rive-list-dispose
Open

mfazekas wants to merge 4 commits into
mainfrom
claude/fix-407-use-rive-list-dispose

Conversation

@mfazekas

@mfazekas mfazekas commented Oct 6, 2026 •

Copy link
Copy Markdown
Collaborator

useRiveList called into its list property after useDisposableMemo had disposed it, which crashes release builds (Nitro only checks for disposed objects in debug). The cleanup no longer calls removeListeners(), and the returned list operations read the property through liveRef like useRiveProperty does, so after disposal they do nothing.

Verified with unit tests and an Android release build (Issue407ListUnmount reproducer).

Not changed here: iOS legacy property dispose() never runs (a throwing protocol-extension method doesn't satisfy Nitro's dispose()), and Android new-runtime properties skip HybridObject.dispose() because the delegated dispose() hides it.

Fixes #407

useRiveList's listener cleanup called property.removeListeners(), but by
then useDisposableMemo may already have disposed the property: on unmount
in release builds dispose() runs synchronously before that cleanup, and on
a dependency change the old property is disposed during render. Nitro
only checks for a disposed object in debug builds, so in release the call
dereferenced a null native object and crashed with SIGSEGV. Native
dispose() already removes the listeners on both platforms, so the call is
dropped.

Fixes #407
…posed

The operations useRiveList returns (getInstanceAt, addInstance,
addInstanceAt, removeInstance, removeInstanceAt, swap) closed over the
property, so one called after unmount or after the instance changed (a
stale closure from a timer or an awaited continuation) called into a
disposed list and crashed release builds the same way as #407. They now
read the property through useDisposableMemo's liveRef, as useRiveProperty
and useRiveTrigger already do: after disposal they warn and resolve
without touching it, and a stale operation after an instance change acts
on the current list.

Also corrects the cleanup comments: only the unsubscribe function returned
by addListener is safe to call there, and it doesn't throw.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

useRiveList: removeListeners() on a disposed list property segfaults on unmount in release builds

1 participant