Repository navigation
🐛 Android: App crashes on device disconnect when using monitorCharacteristicForDevice() #1303
Description
Prerequisites
- I checked the documentation and FAQ without finding a solution
- I checked to make sure that this issue has not already been filed
Bug Description
When using the monitorCharacteristicForDevice() method on Android, if the peripheral device disconnects unexpectedly, the application crashes instead of propagating the disconnection error to the monitoring listener callback. The expected behavior is for the listener (error: BleError | null, characteristic: Characteristic | null) => void to be invoked with a BleError object.
The crash is caused by a java.lang.NullPointerException in the native Android code, as the Promise.reject method is called with a null error code.
Expected Behavior
The listener for monitorCharacteristicForDevice() should be called with an error object detailing the disconnection, and the app should continue running without crashing.
Current Behavior
The application crashes with a FATAL EXCEPTION. The logs indicate a java.lang.NullPointerException because Promise.reject was called with a null code parameter.
Library version
3.5.0
Device
One Plus 12, Android 15
Environment info
- React Native: v0.81
- react-native-ble-plx: v3.5.0
- Platform: Android 15Steps to reproduce
- Connect to a BLE peripheral device.
- Start monitoring a characteristic using
device.monitorCharacteristicForDevice(...). - Force the device to disconnect (e.g., by turning the peripheral off or moving it out of range).
- Observe the application crash.
Proposed Solution / Workaround
The issue appears to be in the onError callback within the monitorCharacteristicForDevice implementation in BlePlxModule.java. The safePromise is rejected with null as the first argument.
A temporary fix is to modify the native file to provide a valid error code string.
File: /node_modules/react-native-ble-plx/android/src/main/java/com/bleplx/BlePlxModule.java
line number: 791
bleAdapter.monitorCharacteristicForDevice(
deviceId, serviceUUID, characteristicUUID, transactionId, subscriptionType,
new OnEventCallback<Characteristic>() {
@Override
public void onEvent(Characteristic data) {
WritableArray jsResult = Arguments.createArray();
jsResult.pushNull();
jsResult.pushMap(characteristicConverter.toJSObject(data));
jsResult.pushString(transactionId);
sendEvent(Event.ReadEvent, jsResult);
}
}, new OnErrorCallback() {
@Override
public void onError(BleError error) {
- safePromise.reject(null, errorConverter.toJs(error));
+ safePromise.reject(error.errorCode.name(), errorConverter.toJs(error));
}
}
);Relevant log output
2025-09-04 15:53:51.086 17235-733 AndroidRuntime com.example.dev E FATAL EXCEPTION: RxComputationThreadPool-3 (Ask Gemini)
Process: com.example.dev, PID: 17235
io.reactivex.exceptions.CompositeException: 2 exceptions occurred.
at io.reactivex.internal.subscribers.LambdaSubscriber.onError(LambdaSubscriber.java:82)
at io.reactivex.internal.operators.flowable.FlowableDoOnEach$DoOnEachSubscriber.onError(FlowableDoOnEach.java:111)
at io.reactivex.internal.operators.flowable.FlowableDoOnLifecycle$SubscriptionLambdaSubscriber.onError(FlowableDoOnLifecycle.java:85)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.checkTerminated(FlowableObserveOn.java:209)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$ObserveOnSubscriber.runAsync(FlowableObserveOn.java:399)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.run(FlowableObserveOn.java:176)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:348)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1156)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:651)
at java.lang.Thread.run(Thread.java:1119)
ComposedException 1 :
com.polidea.rxandroidble2.exceptions.BleDisconnectedException: Disconnected from MAC='XX:XX:XX:XX:XX:XX' with status 8 (GATT_INSUF_AUTHORIZATION or GATT_CONN_TIMEOUT)
at com.polidea.rxandroidble2.internal.connection.RxBleGattCallback$2.onConnectionStateChange(RxBleGattCallback.java:81)
at android.bluetooth.BluetoothGatt$1$4.run(BluetoothGatt.java:390)
at android.bluetooth.BluetoothGatt.runOrQueueCallback(BluetoothGatt.java:1081)
at android.bluetooth.BluetoothGatt.-$$Nest$mrunOrQueueCallback(Unknown Source:0)
at android.bluetooth.BluetoothGatt$1.onClientConnectionState(BluetoothGatt.java:384)
at android.bluetooth.IBluetoothGattCallback$Stub.onTransact(IBluetoothGattCallback.java:209)
at android.os.Binder.execTransactInternal(Binder.java:1523)
at android.os.Binder.execTransact(Binder.java:1457)
Caused by: java.lang.NullPointerException: Parameter specified as non-null is null: method com.facebook.react.bridge.PromiseImpl.reject, parameter code
at com.facebook.react.bridge.PromiseImpl.reject(Unknown Source:2)
at com.bleplx.utils.SafePromise.reject(SafePromise.java:25)
at com.bleplx.BlePlxModule$41.onError(BlePlxModule.java:791)
at com.bleplx.adapter.utils.SafeExecutor.error(SafeExecutor.java:30)
at com.bleplx.adapter.BleModule.lambda$safeMonitorCharacteristicForDevice$45(BleModule.java:1485)
at com.bleplx.adapter.BleModule.$r8$lambda$JVuqIGnSfaxzZFLoyHm91xQUhdI(Unknown Source:0)
at com.bleplx.adapter.BleModule$$ExternalSyntheticLambda3.accept(D8$$SyntheticClass:0)
at io.reactivex.internal.subscribers.LambdaSubscriber.onError(LambdaSubscriber.java:79)
at io.reactivex.internal.operators.flowable.FlowableDoOnEach$DoOnEachSubscriber.onError(FlowableDoOnEach.java:111)
at io.reactivex.internal.operators.flowable.FlowableDoOnLifecycle$SubscriptionLambdaSubscriber.onError(FlowableDoOnLifecycle.java:85)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.checkTerminated(FlowableObserveOn.java:209)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$ObserveOnSubscriber.runAsync(FlowableObserveOn.java:399)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.run(FlowableObserveOn.java:176)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:317)
2025-09-04 15:53:51.086 17235-733 AndroidRuntime com.example.dev E at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:348) (Ask Gemini)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1156)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:651)
at java.lang.Thread.run(Thread.java:1119)
ComposedException 2 :
java.lang.NullPointerException: Parameter specified as non-null is null: method com.facebook.react.bridge.PromiseImpl.reject, parameter code
at com.facebook.react.bridge.PromiseImpl.reject(Unknown Source:2)
at com.bleplx.utils.SafePromise.reject(SafePromise.java:25)
at com.bleplx.BlePlxModule$41.onError(BlePlxModule.java:791)
at com.bleplx.adapter.utils.SafeExecutor.error(SafeExecutor.java:30)
at com.bleplx.adapter.BleModule.lambda$safeMonitorCharacteristicForDevice$45(BleModule.java:1485)
at com.bleplx.adapter.BleModule.$r8$lambda$JVuqIGnSfaxzZFLoyHm91xQUhdI(Unknown Source:0)
at com.bleplx.adapter.BleModule$$ExternalSyntheticLambda3.accept(D8$$SyntheticClass:0)
at io.reactivex.internal.subscribers.LambdaSubscriber.onError(LambdaSubscriber.java:79)
at io.reactivex.internal.operators.flowable.FlowableDoOnEach$DoOnEachSubscriber.onError(FlowableDoOnEach.java:111)
at io.reactivex.internal.operators.flowable.FlowableDoOnLifecycle$SubscriptionLambdaSubscriber.onError(FlowableDoOnLifecycle.java:85)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.checkTerminated(FlowableObserveOn.java:209)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$ObserveOnSubscriber.runAsync(FlowableObserveOn.java:399)
at io.reactivex.internal.operators.flowable.FlowableObserveOn$BaseObserveOnSubscriber.run(FlowableObserveOn.java:176)
at io.reactivex.internal.schedulers.ScheduledRunnable.run(ScheduledRunnable.java:66)
at io.reactivex.internal.schedulers.ScheduledRunnable.call(ScheduledRunnable.java:57)
at java.util.concurrent.FutureTask.run(FutureTask.java:317)
at java.util.concurrent.ScheduledThreadPoolExecutor$ScheduledFutureTask.run(ScheduledThreadPoolExecutor.java:348)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1156)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:651)
at java.lang.Thread.run(Thread.java:1119)
2025-09-04 15:53:51.088 3181-5341 OplusAppStartupMonitor system_server D notifyUnstableAppInfo: Bundle[{unstableTime=1756981431088, reason=crash, userId=0, exceptionMsg=Parameter specified as non-null is null: method com.facebook.react.bridge.PromiseImpl.reject, parameter code, exceptionClass=java.lang.NullPointerException, app_channel_type=unstable, packageName=com.example.dev, unstable_restrict_switch=true}]
2025-09-04 15:53:51.090 3181-5676 OplusEapMa...sendEvent: system_server W com.example.dev happenedcrash
2025-09-04 15:53:51.101 3181-5676 ActivityTaskManager system_server W Force finishing activity com.example.dev/com.nilesecure.MainActivity
Activity
josmithua commentedon Sep 16, 2025
Patch:
react-native-ble-plx+3.5.0.patchkarlvonladach commentedon Sep 17, 2025
This is not the only line to fix. I'm for example using monitorCharacteristic() instead of monitorCharacteristicForDevice(), and it has the same issue.
And actually, all the other functions in this file have the same issue. Once I had a writeCharacteristic() error (due to some malformatted data) and it also made the app crash, with the same error.
fatihkayan20 commentedon Sep 17, 2025
I'm having same issue after upgrading my project to expo 54. I'm using "monitorCharacteristicForService" function.
ERROR Your app just crashed. See the error below.
java.lang.NullPointerException: Parameter specified as non-null is null: method com.facebook.react.bridge.PromiseImpl.reject, parameter code
com.facebook.react.bridge.PromiseImpl.reject(Unknown Source:2)
com.bleplx.utils.SafePromise.reject(SafePromise.java:25)
com.bleplx.BlePlxModule$41.onError(BlePlxModule.java:791)
com.bleplx.adapter.utils.SafeExecutor.error(SafeExecutor.java:30)
com.bleplx.adapter.BleModule.lambda$safeMonitorCharacteristicForDevice$42(BleModule.java:1476)
com.bleplx.adapter.BleModule.$r8$lambda$oguGTpV4M2u9BE2h87wB5iwi2-M(Unknown Source:0)
com.bleplx.adapter.BleModule$$ExternalSyntheticLambda47.run(D8$$SyntheticClass:0)
io.reactivex.internal.operators.flowable.FlowableDoOnLifecycle$SubscriptionLambdaSubscriber.cancel(FlowableDoOnLifecycle.java:115)
io.reactivex.internal.subscribers.BasicFuseableSubscriber.cancel(BasicFuseableSubscriber.java:158)
io.reactivex.internal.subscriptions.SubscriptionHelper.cancel(SubscriptionHelper.java:181)
io.reactivex.internal.subscribers.LambdaSubscriber.cancel(LambdaSubscriber.java:119)
io.reactivex.internal.subscribers.LambdaSubscriber.dispose(LambdaSubscriber.java:104)
com.bleplx.adapter.utils.DisposableMap.removeSubscription(DisposableMap.java:24)
com.bleplx.adapter.BleModule.cancelTransaction(BleModule.java:1044)
com.bleplx.BlePlxModule.cancelTransaction(BlePlxModule.java:134)
com.facebook.jni.NativeRunnable.run(Native Method)
android.os.Handler.handleCallback(Handler.java:942)
android.os.Handler.dispatchMessage(Handler.java:99)
com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.kt:21)
android.os.Looper.loopOnce(Looper.java:201)
android.os.Looper.loop(Looper.java:288)
com.facebook.react.bridge.queue.MessageQueueThreadImpl$Companion.startNewBackgroundThread$lambda$1(MessageQueueThreadImpl.kt:175)
com.facebook.react.bridge.queue.MessageQueueThreadImpl$Companion.$r8$lambda$ldnZnqelhYFctGaUKkOKYj5rxo4(Unknown Source:0)
com.facebook.react.bridge.queue.MessageQueueThreadImpl$Companion$$ExternalSyntheticLambda0.run(D8$$SyntheticClass:0)
java.lang.Thread.run(Thread.java:1119)
josmithua commentedon Sep 17, 2025
I'm very thankful for this package and all the maintainers over the years, but I think it might be time to create a fork if it is going to remain unmaintained. At least I'd be interested in becoming a contributor to help get bug fixes in and released. Bluetooth connectivity is at the core of our work projects and we need a reliable solution that supports the new architecture.
thenjneer commentedon Oct 3, 2025
react-native-ble-plx+3.5.0.patch
For anyone having this issue, here's a working patch^
And here's a writeup (courtesy of mr Claude) for how to use it. @aliberski @angelos3lex this fix is tested working if you plan on updating:
`# Fix for react-native-ble-plx Android Crash on Disconnect
Problem
React Native apps using
react-native-ble-plxcrash on Android when BLE devices disconnect unexpectedly. This is caused byPromise.reject(null)calls in the native Android code, which crashes modern React Native versions that require non-null error codes.Affected versions:
Error symptoms:
characteristic.monitor()operationsRoot Cause
The issue is in
BlePlxModule.javawhere 17 instances of error handling use:React Native's
PromiseImpl.reject()requires non-null error codes, but the BLE library passesnullduring disconnect scenarios.GitHub References:
Solution
Use
patch-packageto fix the native Android code by replacing allnullerror codes with proper error code names.Step 1: Install patch-package
Step 2: Add postinstall script
Add this to your
package.json:{ "scripts": { "postinstall": "patch-package" } }Step 3: Create the patch file
patchesdirectory in your project rootpatches/react-native-ble-plx+3.5.0.patchwith the following content:Step 4: Apply the patch
How It Works
Automatic Application
The patch is automatically applied via the
postinstallscript:npm install- Patch gets applied automaticallynpm ci- Same automatic applicationWhat Gets Fixed
The patch fixes 17 instances in
BlePlxModule.javawhere:This affects all BLE monitoring operations:
characteristic.monitor()callsVerification
Test the fix:
Expected behavior:
Benefits
Compatibility
This fix has been tested and verified to resolve BLE disconnect crashes in production React Native applications.
`
Mr-KID-github commentedon Oct 9, 2025
This patch is working, thank you so much
mrtomhoward commentedon Oct 9, 2025
This patch worked perfectly, thank you @thenjneer!
potocpav commentedon Oct 9, 2025
Works for me as well! ❤️
lamebear commentedon Oct 10, 2025
Thank you!
shynst commentedon Oct 13, 2025
Thank you for taking the effort of digging into this issue which is bugging me just now.
I guess
SafePromiseisn't so safe after all... ;-)I'd like to add something here:
SafePromise.reject()delegates tocom.facebook.react.bridge.Promise.reject()and this is were the exception is thrown. So we need to look further. A simple search for.reject(nullin my IDE revealed 35 occurrences in total. All of those can be replayed with.reject(error.errorCode.name().Hope we are safe again with
SafePromiseafter all.jorismangel commentedon Oct 21, 2025
Hello,
If you having issues applying the patch with patch-package, please follow these steps:
"scripts": { "postinstall": "node fix-ble.js" }to your package.jsonrm -rf node_modules && yarn install(or equivalent with npm)grep "safePromise.reject(error.errorCode.name()" node_modules/react-native-ble-plx/android/src/main/java/com/bleplx/BlePlxModule.java | wc -lto check if the patch has been applied => you should have 17cd android && ./gradlew generateCodegenArtifactsFromSchema && ./gradlew clean && ./gradlew assembleDebug && cd .. && yarn androidHere is the content for fix-ble.js:
Fix Ledger BLE connection crashes on Android RN 0.81+
prathje commentedon Nov 18, 2025
Note that (until this gets merged) you can simply install b1naryth1ef's fork via:
Hot patch an upstream bug. Ref: dotintent/react-native-ble-plx#1303
Zee2 commentedon Dec 23, 2025
Hitting this too - hopefully this will be upstreamed soon. (Until then - maybe a note on the README about known issues with the latest rn versions?)
aliberski commentedon Mar 9, 2026
This is already fixed in the latest version 3.5.1 (PR for reference)