react-native: Android: "FATAL EXCEPTION: mqt_js" / Error calling function: RCTDeviceEventEmitter:emit / - malformed JSON parsing in RN's android native code

Description

Calling fetch() and expecting JSON results (i.e. using response.json()) will crash if server sends malformed JSON. Error/logging system prints “Failed to create String from JSON:” and then does NOT print the JSON (the problem this ticket IS about).

https://github.com/facebook/react-native/issues/9117 promised to fix this, but did not.

Reproduction

Call fetch() and then response.json() with a server that returns bad JSON.

Additional Information

  • React Native version: 0.36
  • Platform: Android
  • Operating System: MacOS

Here’s an example log:

11-04 13:39:46.611  3509  3509 D EPDG -- [EPDGService]: onDataActivity: direction=3
11-04 13:39:46.611  7541  7836 D NetworkController.MobileSignalController(0/2): onDataActivity: direction=3
11-04 13:39:46.781 17917 17917 V MediaPlayer-JNI: getCurrentPosition: 617 (msec)
11-04 13:39:47.031 17917 17917 V MediaPlayer-JNI: getCurrentPosition: 617 (msec)
11-04 13:39:47.281 17917 17917 V MediaPlayer-JNI: getCurrentPosition: 617 (msec)
11-04 13:39:47.541 17917 17917 V MediaPlayer-JNI: getCurrentPosition: 617 (msec)
11-04 13:39:47.641  3509  3509 D EPDG -- [EPDGService]: onDataActivity: direction=1
11-04 13:39:47.641  7541  7836 D NetworkController.MobileSignalController(0/2): onDataActivity: direction=1
11-04 13:39:47.791 17917 17917 V MediaPlayer-JNI: getCurrentPosition: 617 (msec)
11-04 13:39:47.821 17917 17976 I System.out: (HTTPLog)-Static: isSBSettingEnabled false
11-04 13:39:47.821 17917 17976 I System.out: (HTTPLog)-Static: isSBSettingEnabled false
11-04 13:39:47.821  3079  3883 D EnterpriseController: netId is 0
11-04 13:39:47.821  3079  3883 D Netd    : getNetworkForDns: using netid 507 for uid 10726
11-04 13:39:47.821 17917 17968 E AndroidRuntime: FATAL EXCEPTION: mqt_js
11-04 13:39:47.821 17917 17968 E AndroidRuntime: Process: com.stardust, PID: 17917
11-04 13:39:47.821 17917 17968 E AndroidRuntime: java.lang.RuntimeException: Error calling function: RCTDeviceEventEmitter:emit
11-04 13:39:47.821 17917 17968 E AndroidRuntime: 	at com.facebook.react.bridge.queue.NativeRunnable.run(Native Method)
11-04 13:39:47.821 17917 17968 E AndroidRuntime: 	at android.os.Handler.handleCallback(Handler.java:739)
11-04 13:39:47.821 17917 17968 E AndroidRuntime: 	at android.os.Handler.dispatchMessage(Handler.java:95)
11-04 13:39:47.821 17917 17968 E AndroidRuntime: 	at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:31)
11-04 13:39:47.821 17917 17968 E AndroidRuntime: 	at android.os.Looper.loop(Looper.java:158)
11-04 13:39:47.821 17917 17968 E AndroidRuntime: 	at com.facebook.react.bridge.queue.MessageQueueThreadImpl$3.run(MessageQueueThreadImpl.java:196)
11-04 13:39:47.821 17917 17968 E AndroidRuntime: 	at java.lang.Thread.run(Thread.java:818)
11-04 13:39:47.821 17917 17968 E AndroidRuntime: Caused by: java.lang.RuntimeException: Failed to create String from JSON: 
11-04 13:39:47.821 17917 17968 E AndroidRuntime: 	... 7 more
11-04 13:39:47.841  3509  4040 W ActivityManager:   Force finishing activity com.stardust/.MainActivity
11-04 13:39:47.851  3509  4040 D FocusedStackFrame: Set to : 0
11-04 13:39:47.851  3509  4040 D InputDispatcher: Focused application set to: xxxx
11-04 13:39:47.861  3509  4040 D InputDispatcher: Focus left window: 17917
11-04 13:39:47.861  3509  3638 D PointerIcon: setMouseIconStyle1 pointerType: 1001 iconType:101 flag:0 pid:3509 uid:1000
11-04 13:39:47.861  3509  3638 D PointerIcon: setMouseCustomIcon IconType is same.101
11-04 13:39:47.931  3509 18371 E android.os.Debug: ro.product_ship = true
11-04 13:39:47.931  3509 18371 E android.os.Debug: ro.debug_level = 0x4f4c
11-04 13:39:47.931  3509 18371 E android.os.Debug: sys.mobilecare.preload = false
11-04 13:39:47.971  3509  3610 D ViewRootImpl: #1 mView = com.android.internal.policy.PhoneWindow$DecorView{5822614 V.E...... R.....I. 0,0-0,0}
11-04 13:39:47.981  3509  3610 D ISSUE_DEBUG: InputChannelName : 1cdbcb2 Application Error: com.stardust
11-04 13:39:47.981  3509  3610 V MARsPolicyManager: handelAlertToastWindowStarted pkgName = android
11-04 13:39:47.981  3509  3610 D InputDispatcher: Focus entered window: 3509
11-04 13:39:47.981  3509  3638 D PointerIcon: setMouseIconStyle1 pointerType: 1001 iconType:101 flag:0 pid:3509 uid:1000
11-04 13:39:47.981  3509  3638 D PointerIcon: setMouseCustomIcon IconType is same.101
11-04 13:39:47.991  3067  3067 I SurfaceFlinger: id=2946 createSurf (193x193),1 flag=4, ttardust
11-04 13:39:48.001 18372 18372 E Zygote  : v2
11-04 13:39:48.001 18372 18372 I libpersona: KNOX_SDCARD checking this for 1000
11-04 13:39:48.001  3509  3609 I ActivityManager: Start proc 18372:com.samsung.android.sm/1000 for broadcast-3 com.samsung.android.sm/.common.SmartManagerReceiver
11-04 13:39:48.001 18372 18372 I libpersona: KNOX_SDCARD not a persona

Questions:

  • What do we have to do to actually include the JSON so we can get to the bottom of this?
  • Why does it say “… 7 more” ?

About this issue

  • Original URL
  • State: closed
  • Created 8 years ago
  • Reactions: 10
  • Comments: 49 (8 by maintainers)

Commits related to this issue

Most upvoted comments

it happens when I do

fetch('http://ideapit.net/filedata/201203/9fi32220120330230712.jpg')

and it’s only crash on Android release mode.

Hence, I switched to axios and since then no more crash!

This expo app demonstrates the issue, and that the same code works on iOS: https://snack.expo.io/HJkNh3_Sz

Deeper analysis shows that the android network module retrieves the data and sends to the NativeEventEmitter: https://github.com/facebook/react-native/blob/3fc33bb54fc5dcf7ef696fe245addc320f85a269/ReactAndroid/src/main/java/com/facebook/react/modules/network/NetworkingModule.java#L390

It is then somewhere in the bridge code where the crash occurs. In the debugger I don’t get any further than here: https://github.com/facebook/react-native/blob/3fc33bb54fc5dcf7ef696fe245addc320f85a269/ReactAndroid/src/main/java/com/facebook/react/bridge/JavaScriptModuleRegistry.java#L91-L97

where the Java arguments are converted to a C++ object for the invocation of the bridged function. The crash stacktrace comes from the C++ jsc helper code: https://github.com/facebook/react-native/blob/3fc33bb54fc5dcf7ef696fe245addc320f85a269/ReactCommon/jschelpers/Value.cpp#L69-L77

As the crash is in the JS bridge, this bug could also affect other native modules which do not sanitise data before sending it to Javascript.

We came across this issue yesterday, when the download of a configuration file for the app caused 5000+ crashes.

We traced the cause of the crash to a misconfiguration on the CDN, meaning a gzipped file was transmitted without a Content-Encoding: gzip header. This led to a fatal error when react-native tried to pass this raw data to the Javascript context. As above the crash stacktrace is:

Error calling RCTDeviceEventEmitter.emit

Failed to create Value from JSON: 
run
    NativeRunnable.java
handleCallback
    Handler.java:789
dispatchMessage
    Handler.java:98
dispatchMessage
    MessageQueueThreadHandler.java:31
loop
    Looper.java:164
run
    MessageQueueThreadImpl.java:194
run
    Thread.java:764

We reproduced this issue with the latest react-native version (0.52.1) from a clean project, created with react-native init by adding the following code to index.js:

fetch('https://cdn.cliqz.com/test/rn_test/rn_crash_gzip.json.gz').then((resp) => {
  console.log('crash happens after this');
})

This file is a gzipped json file with the server configured to not send a Content-Encoding header. Note, this code on iOS only produces a warning: screen shot 2018-01-25 at 12 41 17

We successfully minimised this test file to 3 bytes which could trigger the crash on android:

$ hexdump rn_crash.txt
0000000 ed ba 98
0000003

Using a file with these contents (available at https://cdn.cliqz.com/test/rn_test/rn_crash.txt) will also cause a crash with the above code. Note, with this case there is no warning on iOS, leading us to think that there are multiple problematic byte sequences we would cause the crash.

More: This seems to only happen in release-mode i.e. react-native run-android --variant=release or install via APK.

It does not happen in debug mode.

I narrowed it down.

BUG: Using fetch() and then doing response.json() when the server sends malformed JSON causes a crash.

Expectation: JS exception, not crash.

On Nov 6, 2016 10:04 AM, “Brent Vatne” notifications@github.com wrote:

Hello! You’re unlikely to get much help until you can provide a reproducible example 😦 Please keep trying and let us know when you have it.

— You are receiving this because you authored the thread. Reply to this email directly, view it on GitHub https://github.com/facebook/react-native/issues/10756#issuecomment-258698241, or mute the thread https://github.com/notifications/unsubscribe-auth/AAXXZY1Mr-JMwYXdY0poY0JMccwNIGZ_ks5q7ha2gaJpZM4Kp-dh .

Okay bot., here is further activity.

It’s a bug caused by the following code:

Value Value::fromDynamic(JSContextRef ctx, const folly::dynamic& value) {
// JavaScriptCore's iOS APIs have their own version of this direct conversion.
// In addition, using this requires exposing some of JSC's private APIs,
//  so it's limited to non-apple platforms and to builds that use the custom JSC.
// Otherwise, we use the old way of converting through JSON.
#if USE_FAST_FOLLY_DYNAMIC_CONVERSION
  // Defer GC during the creation of the JSValue, as we don't want
  //  intermediate objects to be collected.
  // We could use JSValueProtect(), but it will make the process much slower.
  JSDeferredGCRef deferGC = JSDeferGarbageCollection(ctx);
  // Set a global lock for the whole process,
  //  instead of re-acquiring the lock for each operation.
  JSLock(ctx);
  JSValueRef jsVal = Value::fromDynamicInner(ctx, value);
  JSUnlock(ctx);
  JSResumeGarbageCollection(ctx, deferGC);
  return Value(ctx, jsVal);
#else
  auto json = folly::toJson(value);
  return fromJSON(String(ctx, json.c_str()));
#endif
}

folly:toJson produces a string that cannot be decoded by String(ctx, json.c_str()) with utf8 encoding.

if we change the code to:

  folly::json::serialization_opts opts;
  opts.encode_non_ascii = true;
  opts.skip_invalid_utf8 = true;
  auto json = folly::json::serialize(value, opts);

It will not crash. But the result may be incorrect.

I got the same crash. "react-native": "0.42.3" when I do this on Android. fetch('http://s1.dwstatic.com/group1/M00/F5/15/af09c9472255fc101863cf4929a4daff.gif')

I try to use axios, it’s correct.

Hey there, I just got the same error.

All working properly on iOS but it doesn’t work with Android. I’m getting this with

generateImage(id) {
  var self = this;
  this.setState({
    image: ''
  }, () => {
    var uri = 'https://source.unsplash.com/' + Dimensions.get('window').width + 'x' + Dimensions.get('window').height + '/?restaurants#ignoreCacheFragment=' + Number(Date.now());
  
    fetch(uri)  
    .then((response) => {
    
      uri = response.url;
      this.setState({
        images: self.state.images.concat([uri]),
        image: uri,
        lastId: self.state.lastId++,
      });
    }).catch(error => console.log(error));
  });
}

Ignoring the other parts which are not causing this issue what you get here is a 302 HTTP response from unsplash. You can try with a static url such as:

$ curl https://source.unsplash.com/1600x900/?restaurants -i

HTTP/1.1 302 Found
Server: Cowboy
Date: Thu, 15 Dec 2016 23:16:36 GMT
Connection: keep-alive
X-Frame-Options: SAMEORIGIN
X-Xss-Protection: 1; mode=block
X-Content-Type-Options: nosniff
Location: https://images.unsplash.com/photo-1429681601148-75510b2cef43?ixlib=rb-0.3.5&q=80&fm=jpg&crop=entropy&cs=tinysrgb&w=1600&h=900&fit=crop&s=2c9a1a9c3bb523bef3ee6b07aba56cfa
Content-Type: text/html; charset=utf-8
Cache-Control: no-cache
X-Request-Id: 6a9fa9a4-8c56-451e-b20a-569f86f2b2d0
X-Runtime: 0.135146
Strict-Transport-Security: max-age=31536000
Vary: Origin
Transfer-Encoding: chunked
Via: 1.1 vegur

<html><body>You are being <a href="https://images.unsplash.com/photo-1429681601148-75510b2cef43?ixlib=rb-0.3.5&amp;q=80&amp;fm=jpg&amp;crop=entropy&amp;cs=tinysrgb&amp;w=1600&amp;h=900&amp;fit=crop&amp;s=2c9a1a9c3bb523bef3ee6b07aba56cfa">redirected</a>.</body></html>

On iOS I can properly access the raw response object. On Android the error is the same as the OP, it crashes before to even resolve the promise (it’s not related to any call inside the promise, the promise never runs).

I was digging into this problem but I’m unsure if with Android (5.1.1 here for tests) it finds a native implementation or it’s using the whatwg-fetch (tbh, I don’t quite understand where in react native could it be found natively – i’d say it’s always from the library, am I wrong?).

I’m new into looking at RN codebase if you can give to me some advise I can try to get more info.

Crash on Android, but OK on iOS

fetch(pngURL)
  .then(response => response.text())
  .then(base64Data => {});

Just for the bot. No activity means it’s not solved.

any progress on this issue.

react-native: 0.52.2, a

the exception can not be catched which crash the whole app

Was trying to do some web scrapping and same problem here with RN 0.51 on Android. Response is a 200 (no redirect) with HTML content and it works perfectly with jest, but once it’s running on Android, it red-screen crashes.

~What I’m not sure is if it crashes because of the fetch since I’m using response.text() and not response.json() or on JSON.parse.~

OK, definitely this is because of some problem inside fetch.

After further debugging, it seems it was a problem with the charset response from the server. Adding the following header, fixed this issue for me:

const opts = {
  headers: {
    'accept': 'text/html; charset=utf-8'
  }
}

const response = await fetch(url, opts);

hi guys.same here when I tried to fetch GooglePlacePhoto api. the response is an image/jpeg type with other informations about the image (so called malformed JSON??). I’m using the RN0.47 and the bug only happened in android.

this bug has been mentioned since almost half year but yet not a solution .How did you guys solved it???

my code is like this:

  let initOption = {
    mode: 'cors',
    cache: 'default',
    method: 'GET',
  };

  try {
    let response = await fetch('https://maps.googleapis.com/maps/api/place/photo?'+ Qs.stringify({
      key: config.googlePlacesKey,
      maxwidth: 150,
      photoreference: photo.photo_reference,
    }), initOption);

    return await response;

  } catch(error) {
    console.error(error);
  }

OK, I finally used the XMLHttpRequest API instead of fetch to get rid of this in android…

This is happening to me on android 5.0.1 when attempting to fetch(URL) an image.

It seems to be happening due to lack of support for ‘blob’. I feel like this should be documented in the networking page since the tutorial seems to indicate you can use the fetch api in it’s entirety.

A workaround is to use the react-native-fetch-blob library.

Both

On Nov 6, 2016 10:14 AM, “Brent Vatne” notifications@github.com wrote:

@GeoffreyPlitt https://github.com/GeoffreyPlitt - that’s no fun. Does that happen in release mode or debug or both?

— You are receiving this because you were mentioned. Reply to this email directly, view it on GitHub https://github.com/facebook/react-native/issues/10756#issuecomment-258698917, or mute the thread https://github.com/notifications/unsubscribe-auth/AAXXZcA_h-an57Pav1_aHGGQNebkhVPVks5q7hkGgaJpZM4Kp-dh .