react-native: WebSocket module for Android crashes app when it fails to connect

When creating a new WebSocket connection, if the var ws = new WebSocket('....'); call fails to connect to the server, it will crash the app by throwing an error with message Cannot close WebSocket. Unknown WebSocket id 0

This is because the websocketFailed event fires, which calls this._closeWebSocket(id); inside WebSocket.js. Since the connection was never open to begin with, calling WebSocket client = mWebSocketConnections.get(id); inside WebSocketModule.java will return null, hence triggering the exception throw new RuntimeException("Cannot close WebSocket. Unknown WebSocket id " + id);

About this issue

  • Original URL
  • State: closed
  • Created 9 years ago
  • Comments: 50 (13 by maintainers)

Commits related to this issue

Most upvoted comments

my problem : in javascript, the socket instance is connected and readyState is OPEN, but when i send a message, android app crash my solution is copy WebSocketModule.java as a CustomModule

if (client == null) {
            // This is a programmer error
            //throw new RuntimeException("Cannot send a message. Unknown WebSocket id " + id);
            mWebSocketConnections.remove(id);
            notifyWebSocketFailed(id, "Unknown WebSocket id");
            return;
        }

@tanthanh289 can you send a pull request please? it takes very few minutes to send a PR with your changes if you have already fixed it and helps everyone.

Reopening since this is somewhat core and results in crashes.

@satya164, I’ve created PR #17884 to address this issue. Would like to greatly credit @tanthanh289 for inspiring this fix.

I’m a Javascript developer, no Java developer. The fact that I understand Java doesn’t mean I’m capable of analyzing the situation and provide a propper solution. I just wanted to point out, that currently there shouldn’t be a single RN app using RN’s websockets in the store, as they just break the app.

I have found one of problems is due to HashMap used for mWebSocketConnections in WebSocketModule.java.

HashMap is not thread-safe, and can lose socket clients if concurrent sockets are opened at the same time.

If you can build RN from android source, just try this:

import java.util.concurrent.ConcurrentHashMap;

// change from HashMap to ConcurrentHashMap
private final Map<Integer, WebSocket> mWebSocketConnections = new ConcurrentHashMap<>();
private final Map<Integer, ContentHandler> mContentHandlers = new ConcurrentHashMap<>();

// and more safety:
  @ReactMethod
  public void send(String message, int id) {
    WebSocket client = mWebSocketConnections.get(id);
    if (client == null) {
      // This is a programmer error
      // throw new RuntimeException("Cannot send a message. Unknown WebSocket id " + id);

        // just notify error
        WritableMap params = Arguments.createMap();
	    params.putInt("id", id);
	    params.putString("message", "client is null");
	    sendEvent("websocketFailed", params);
	    params = Arguments.createMap();
	    params.putInt("id", id);
	    params.putInt("code", 0);
	    params.putString("reason", "client is null");
	    sendEvent("websocketClosed", params);
	    mWebSocketConnections.remove(id);
	    mContentHandlers.remove(id);
        return;
    }
    try {
      client.send(message);
    } catch (Exception e) {
      notifyWebSocketFailed(id, e.getMessage());
    }
  }

We are also experiencing this crash. It happens quite a lot , about 2%-3% of the sessions, to thousands of users. It seems like this is an ongoing issue, maybe it should be re-open?

We have hit this as well in our app that is currently in beta, but it is our second most hit crash already. We use websockets in a couple places, and we do the correct hardening in those locations in the javascript. It seems like a bug that the native layer would throw such an exception

The native layer still crashes with no way to prevent it from js. this happens for me when I enable airplane mode after making a websocket connection. I send a keepalive message every few seconds and when it fails because there is no connection I get this error. RN ~0.42.3

09-04 16:54:00.198 23229 23262 E AndroidRuntime: java.lang.RuntimeException: Cannot send a message. Unknown WebSocket id 2
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at com.facebook.react.modules.websocket.WebSocketModule.send(WebSocketModule.java:221)
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at java.lang.reflect.Method.invoke(Native Method)
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at com.facebook.react.bridge.BaseJavaModule$JavaMethod.invoke(BaseJavaModule.java:345)
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at com.facebook.react.cxxbridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:136)
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at android.os.Handler.handleCallback(Handler.java)
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java)
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:31)
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java)
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:196)
09-04 16:54:00.198 23229 23262 E AndroidRuntime: 	at java.lang.Thread.run(Thread.java:818)
09-04 16:54:00.208  3551  4476 W ActivityManager:   Force finishing activity com.chipta/.MainActivity

I tried a try / catch around the ws.send and also a if this.ws but they both don’t prevent this error so I’m kind of stuck. How do I check if I can send a message if the ws object does not give me this information?

edit: It seems possible to prevent this if I check for an internet connection before sending. But it should just raise a js error like a normal http request

Please stop commenting with +1. It sends a notification to everyone in the thread and doesn’t achieve anything to resolve the issue.

Many people have commented here with the fix, (especially @tanthanh289) and it’ll be useful if someone sends a pull request with that change to fix it. If this issue affects you, please send a pull request and mention me, and I’ll try to get it merged.

Copy the source java code as your custom socket module and fix the bug

Really strange that you copy the code to fix it, but don’t wanna send a pull request.

This really needs to be adressed. Apps using the react native websockets in this state just can’t be published, because they are simply broken. You always can check the ready state before sending, but it seems that this isn’t really reliable. Then when you socket.send() you get the crash.

I was thinking of just forking RN and just catching this exception natively

Or just send a PR?

My comment already states how to prevent the crash:

It seems possible to prevent this if I check for an internet connection before sending. But it should just raise a js error like a normal http request

This should still error in the js and not in the java. Java errors are impossible to deal with from js. So I don’t get why this issue was closed. @ide

It’s still happening

@danieldelouya Copy the source java code as your custom socket module and fix the bug, copy the source javascript code as your custom socket component, it required your customer socket module, if the platform === ios return default socket, else return your custom socket, then where you need socket, require your custom socket

************* Crash Log Head **************** Device Manufacturer: Freescale Device Model : INBOX310 Android Version : 4.2.2 Android SDK : 17 App VersionName : 0.8.7 App VersionCode : 2 ************* Crash Log Head ****************

java.lang.RuntimeException: Cannot send a message. Unknown WebSocket id 0 at com.facebook.react.modules.websocket.WebSocketModule.send(WebSocketModule.java:199) at java.lang.reflect.Method.invokeNative(Native Method) at java.lang.reflect.Method.invoke(Method.java:511) at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:363) at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:162) at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method) at android.os.Handler.handleCallback(Handler.java:725) at android.os.Handler.dispatchMessage(Handler.java:92) at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:31) at android.os.Looper.loop(Looper.java:137) at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:194) at java.lang.Thread.run(Thread.java:856)

This issue might be related to #8949

Two things we should do: fix the bug and make the native layer uncrashable.