App crashes on startup with: [Worklets] Tried to synchronously call a non-worklet function addListener on the UI thread when upgrading to Expo sdk 55. #9023
Replies: 5 comments 2 replies
-
|
Hi @miguelartazos. I don't think that I'm strongly against the global singleton - the point of |
Beta Was this translation helpful? Give feedback.
-
|
Hi Tomasz, thank you so much for your response. My Metro configuration is
standard Expo SDK 55 + Sentry + Uniwind — no custom resolver modifications
that would affect
.native.ts resolution. The platforms config is set correctly by Expo.
The issue isn't that Metro picks the wrong file systematically. It's that
the library ships a NOOP .ts alongside a real
.native.ts, and any Metro cache/fast-refresh edge case that loads both
creates a silent cache coherency failure → crash. The
NOOP's set() being a no-op and get() returning null! means there's zero
fault tolerance.
The global singleton via globalThis ensures both files share the same
WeakMap regardless of which gets loaded. This isn't an
anti-pattern — it's a defensive fix for a library design that assumes
Metro will never resolve the wrong file, which isn't
guaranteed.
Do you have any idea of what else could be causing this error? Thank you
again this issue has been annoying me for the past 2 days.
…On Fri, Feb 27, 2026 at 12:25 PM Tomasz Żelawski ***@***.***> wrote:
Hi @miguelartazos <https://github.com/miguelartazos>. I don't think that
serializableMappingCache.ts should ever be picked in favor of
serializableMappingCache.native.ts during native development - it seems
like a problem with your Metro/Expo configuration.
I'm strongly against the global singleton - the point of .native.ts and
.ts implementations are to prevent such anti-patterns in the codebase and
ensure proper encapsulation.
—
Reply to this email directly, view it on GitHub
<#9023 (comment)>,
or unsubscribe
<https://github.com/notifications/unsubscribe-auth/BUCMS75QWIVNFLPKDBHWEUL4OCDZ3AVCNFSM6AAAAACWBUDF3KVHI2DSMVQWIX3LMV43URDJONRXK43TNFXW4Q3PNVWWK3TUHMYTKOJUHE4TANQ>
.
You are receiving this because you were mentioned.Message ID:
<software-mansion/react-native-reanimated/repo-discussions/9023/comments/15949906
@github.com>
|
Beta Was this translation helpful? Give feedback.
-
|
I'm also running into the same issue here but the op's workaround couldn't fix mine. I've been migrating my bare react native app to expo 55 and struggling with this one for a week. One thing I did notice here is these lines in metro.config.js
if I use getDefaultConfig from expo/metro-config, I'll get the error, while using getDefaultConfig from @react-native/metro-config won't face the issue. But doing so will compromise my intent to migrate fully to expo. |
Beta Was this translation helpful? Give feedback.
-
|
hey, I don't know if you OP here has react-native-dotenv installed in your project, but replacing react-native-dotenv by react-native- config fixed the issue for me. |
Beta Was this translation helpful? Give feedback.
-
|
I fixed this issue when removed react-native-dotenv library from my project |
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Environment
Root cause (investigation)
We traced the crash to
createMapperRegistry().start()inmappers.tscallingsv.addListener()on shared values that were serialized field-by-field instead of via the__init→legacy_makeMutableUIpath. As a result,addListenerbecomes a RemoteFunction stub and throws when called on the UI thread.Hypothesis:
serializableMappingCacheis split between two implementations:serializableMappingCache.ts— NOOP stub (setdoes nothing,getreturns null)serializableMappingCache.native.ts— real WeakMapIf Metro resolves Reanimated’s
mutables.tsto the NOOP cache and Worklets’serializable.native.tsto the real cache, then:makeMutableNativewrites to the NOOP cache (no-op)createSerializablereads from the real cache (empty)clonePlainJSObject→addListenerbecomes RemoteFunction → crashDiagnostic evidence:
sv.addListenerhasisRemote: trueat the crash sitecreateSerializableran beforemutables.tsloaded in our tests (load order)Workaround
Using a global singleton for the cache fixes the crash:
// serializableMappingCache.native.ts and serializableMappingCache.ts
const cache = ((globalThis as any).__workletsSerializableCache ??= new WeakMap());
Beta Was this translation helpful? Give feedback.
All reactions