Description
When any component using KeyboardProvider / KeyboardAwareScrollView mounts, the dev console is flooded with
Reanimated/Worklets warnings, and a worklet throws in development:
[Worklets] Tried to synchronously call a non-worklet function `addListener` on the UI thread.
This happens on both iOS and Android, on app startup (the providers wrap the root), and repeats on every screen
that mounts a keyboard-aware view. The app remains functional (the thrown error is shown as a dev redbox/LogBox), but
it makes development very noisy and is non-suppressible from app code, since the throw originates inside
react-native-worklets.
Environment
| Package |
Version |
| react-native-keyboard-controller |
1.21.8 |
| react-native |
0.85.3 |
| react-native-reanimated |
4.3.1 |
| react-native-worklets |
0.8.3 |
| react-native-screens |
4.25.1 |
| react-native-safe-area-context |
5.7.0 |
| react |
19.2.3 |
| expo |
56.0.3 (SDK 56) |
| Architecture |
New Architecture (Fabric/bridgeless), Hermes |
| Platforms |
iOS (Simulator + device) and Android (emulator) |
| Babel |
babel-preset-expo (injects react-native-worklets/plugin automatically; confirmed not double-applied) |
Steps to reproduce
- Expo SDK 56 / RN 0.85 app with the New Architecture, Reanimated 4.3.1 + Worklets 0.8.3.
- Wrap the app root in
<KeyboardProvider> and render any screen using KeyboardAwareScrollView (e.g. a simple
login form).
- Run a dev build (
expo run:ios / expo run:android) and open the screen.
Minimal component:
import { KeyboardProvider, KeyboardAwareScrollView } from 'react-native-keyboard-controller';
export default function App() {
return (
<KeyboardProvider>
<KeyboardAwareScrollView>
<TextInput placeholder="email" />
<TextInput placeholder="password" secureTextEntry />
</KeyboardAwareScrollView>
</KeyboardProvider>
);
}
Expected behavior
Keyboard-aware scrolling works with no Worklets warnings or thrown errors.
Actual behavior
A burst of warnings (8+ per mount):
WARN [Worklets] Tried to modify key `value` of an object which has been already passed to a worklet.
https://docs.swmansion.com/react-native-reanimated/docs/guides/troubleshooting#tried-to-modify-key-of-an-object-which
-has-been-converted-to-a-serializable
followed by a thrown error:
ERROR [Error: [Worklets] Tried to synchronously call a non-worklet function `addListener` on the UI thread.
https://docs.swmansion.com/react-native-worklets/docs/guides/troubleshooting#tried-to-synchronously-call-a-non-workle
t-function-on-the-ui-thread]
A representative call-stack frame points at valueUnpacker (worklet value deserialization on the UI runtime).
Investigation / likely cause
Both messages originate in react-native-worklets 0.8:
"Tried to modify key 'value' …" → logger.warn in
react-native-worklets/lib/module/memory/serializable.native.js (~line 542).
"… non-worklet function 'addListener' on the UI thread" → thrown in
react-native-worklets/lib/module/threads.native.js (~line 188; in dev a remote/JS function called on the UI runtime
throws to point users at runOnJS).
It appears the keyboard handler captures the FocusedInputEvents event-emitter object (which exposes addListener)
into a worklet/shared value, and worklets 0.8's stricter serialization then (a) warns when that captured object is
mutated, and (b) throws when addListener is invoked during unpacking on the UI thread. The relevant site in this
package — react-native-keyboard-controller/lib/module/animated.js:
import Reanimated, { useSharedValue } from "react-native-reanimated";
import { FocusedInputEvents, /* ... */ } from "./bindings";
// ...
const progressSV = useSharedValue(0);
const heightSV = useSharedValue(0);
const layout = useSharedValue(null);
// ...
const subscription = FocusedInputEvents.addListener("layoutDidSynchronize", () => { /* ... */ });
(plus several "worklet" handlers in the same file). This pattern worked under older worklets versions but trips
worklets 0.8's serialization rules under the New Architecture.
Workaround
configureReanimatedLogger({ strict: false }) quiets the warnings but not the thrown error. We replaced
KeyboardAwareScrollView/KeyboardProvider with a small wrapper over React Native's KeyboardAvoidingView +
ScrollView, which avoids worklets entirely.
Additional notes
- Reproduces with the exact versions Expo SDK 56 pins (
expo install reports them as up to date), so it isn't a
user version mismatch.
- Dev-only (
__DEV__) — release builds don't surface the messages, but the underlying worklet behavior is still a
smell.
- Ruled out: verified a single instance of react, react-native, react-native-reanimated, and react-native-worklets
(single hoisted copy, no nested duplicates, no self-reference symlinks, default Metro resolver, Yarn 1 workspace).
So this is not the duplicate-instance / self-referencing-package class of issue that's commonly blamed for these
worklet errors.
- Happy to provide a minimal reproducible repo if helpful.
Description
When any component using
KeyboardProvider/KeyboardAwareScrollViewmounts, the dev console is flooded withReanimated/Worklets warnings, and a worklet throws in development:
This happens on both iOS and Android, on app startup (the providers wrap the root), and repeats on every screen
that mounts a keyboard-aware view. The app remains functional (the thrown error is shown as a dev redbox/LogBox), but
it makes development very noisy and is non-suppressible from app code, since the throw originates inside
react-native-worklets.Environment
babel-preset-expo(injectsreact-native-worklets/pluginautomatically; confirmed not double-applied)Steps to reproduce
<KeyboardProvider>and render any screen usingKeyboardAwareScrollView(e.g. a simplelogin form).
expo run:ios/expo run:android) and open the screen.Minimal component:
Expected behavior
Keyboard-aware scrolling works with no Worklets warnings or thrown errors.
Actual behavior
A burst of warnings (8+ per mount):
followed by a thrown error:
A representative call-stack frame points at
valueUnpacker(worklet value deserialization on the UI runtime).Investigation / likely cause
Both messages originate in react-native-worklets 0.8:
"Tried to modify key 'value' …"→logger.warninreact-native-worklets/lib/module/memory/serializable.native.js(~line 542)."… non-worklet function 'addListener' on the UI thread"→ thrown inreact-native-worklets/lib/module/threads.native.js(~line 188; in dev a remote/JS function called on the UI runtimethrows to point users at
runOnJS).It appears the keyboard handler captures the
FocusedInputEventsevent-emitter object (which exposesaddListener)into a worklet/shared value, and worklets 0.8's stricter serialization then (a) warns when that captured object is
mutated, and (b) throws when
addListeneris invoked during unpacking on the UI thread. The relevant site in thispackage —
react-native-keyboard-controller/lib/module/animated.js:(plus several
"worklet"handlers in the same file). This pattern worked under older worklets versions but tripsworklets 0.8's serialization rules under the New Architecture.
Workaround
configureReanimatedLogger({ strict: false })quiets the warnings but not the thrown error. We replacedKeyboardAwareScrollView/KeyboardProviderwith a small wrapper over React Native'sKeyboardAvoidingView+ScrollView, which avoids worklets entirely.Additional notes
expo installreports them as up to date), so it isn't auser version mismatch.
__DEV__) — release builds don't surface the messages, but the underlying worklet behavior is still asmell.
(single hoisted copy, no nested duplicates, no self-reference symlinks, default Metro resolver, Yarn 1 workspace).
So this is not the duplicate-instance / self-referencing-package class of issue that's commonly blamed for these
worklet errors.