Troubleshooting
Common errors and what to check first.
Nothing renders on the secondary runtime
Check, in order:
index.jsloads the generated entry. It must require.threaded-runtime/entrywhenglobal.__THREADED_RUNTIME_ENV__is set. See Metro setup.- The component is at module scope. Inline or nested components aren't registered.
- The Metro plugin sees the file. It must be under one of the configured
rootsinmetro.config.js.
"Could not find Nitro module" on Android
Add NitroModulesPackage() (and ThreadedZustandPackage() if you use shared
state) to setExtraReactPackagesProvider in your MainApplication. See
Android setup.
iOS build fails with _NativeComposeThreadedRuntime symbol error
Re-run pod install from ios/, then Product → Clean Build Folder in
Xcode. If you've recently switched between simulators and devices, also
delete ~/Library/Developer/Xcode/DerivedData.
Prewarm seems to have no effect
prewarm is a hint, not a blocking start. If you call it and immediately
mount a surface, the surface mounts before the runtime is fully started. The
result is still correct, but the first mount waits for startup anyway.
To remove that wait, prewarm earlier — from a parent screen, an app-launch hook, or native startup.
A threaded screen renders blank
- Did the props change between mounts? Verify they're JSON-serializable.
- Is the runtime name a stable string? A new name every render = a new runtime per mount.
- Is the threaded entry file generated (
.threaded-runtime/entry.jsexists after a Metro run)?
A headless task never runs
- Confirm
registerThreadedHeadlessTask('name', ...)is loaded inside the threaded bundle (i.e. it's reachable from a Metro root, or registered inindex.<runtime>.ts). - The task name must match exactly between
registeranddispatch/runHeadlessTask. - The promise returned by
runHeadlessTaskresolves on dispatch, not on completion — observe progress through shared state.
Two runtimes overwrite each other's writes
Use update(fn) or a path reducer instead of set(value). See
Locking and revisions.
'background' directive isn't being rewritten
- The function must be declared at module scope (not inside another function or component).
- The directive must be the first statement in the function body.
- The function must be defined in a file under one of the Metro plugin's
roots.
When in doubt, inspect the generated entry
Open .threaded-runtime/entry.js after a Metro run. It lists every
registered component and runtime function — if yours isn't there, the
plugin didn't pick it up.