react-native icon indicating copy to clipboard operation
react-native copied to clipboard

UIImplementation.createView "Root node with tag 21 doesn't exist " for both debug builds and prod builds

Open simonxcheng opened this issue 9 months ago • 11 comments

Description

If an apk is debug enabled, one out of 3 times, the first load on physical devices (Pixel 5, Samsung S20 Ultra, etc.) cause errors due to "no active CatalystInstance". RN error screen also shows "Root node with tag 21 doesn't exist". It only happens on the first load. Sub-sequential loads have no issue. [Update: we observed it is repeated issue on production!]

First error call stack:

Unhandled SoftException com.facebook.react.bridge.ReactNoCrashSoftException: No active CatalystInstance, cannot emitUpdateDimensionsEvent at com.facebook.react.modules.deviceinfo.DeviceInfoModule.emitUpdateDimensionsEvent(DeviceInfoModule.java:94) at com.facebook.react.ReactRootView$CustomGlobalLayoutListener.emitUpdateDimensionsEvent(ReactRootView.java:1075) at com.facebook.react.ReactRootView$CustomGlobalLayoutListener.checkForDeviceDimensionsChanges(ReactRootView.java:1032) at com.facebook.react.ReactRootView$CustomGlobalLayoutListener.onGlobalLayout(ReactRootView.java:916) at android.view.ViewTreeObserver.dispatchOnGlobalLayout(ViewTreeObserver.java:1084) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4302) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3116) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:10885) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1301) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1309) at android.view.Choreographer.doCallbacks(Choreographer.java:923) at android.view.Choreographer.doFrame(Choreographer.java:852) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1283) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8762) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

Second error call stack:

com.facebook.react.bridge.ReactNoCrashSoftException: Cannot get UIManager because the context doesn't contain an active CatalystInstance. at com.facebook.react.uimanager.UIManagerHelper.getUIManager(UIManagerHelper.java:77) at com.facebook.react.uimanager.UIManagerHelper.getEventDispatcher(UIManagerHelper.java:128) at com.facebook.react.uimanager.UIManagerHelper.getEventDispatcherForReactTag(UIManagerHelper.java:106) at com.th3rdwave.safeareacontext.SafeAreaProviderManagerKt.handleOnInsetsChange(SafeAreaProviderManager.kt:39) at com.th3rdwave.safeareacontext.SafeAreaProviderManagerKt.access$handleOnInsetsChange(SafeAreaProviderManager.kt:1) at com.th3rdwave.safeareacontext.SafeAreaProviderManager$addEventEmitters$1.invoke(SafeAreaProviderManager.kt:28) at com.th3rdwave.safeareacontext.SafeAreaProviderManager$addEventEmitters$1.invoke(SafeAreaProviderManager.kt:28) at com.th3rdwave.safeareacontext.SafeAreaProvider.maybeUpdateInsets(SafeAreaProvider.kt:21) at com.th3rdwave.safeareacontext.SafeAreaProvider.onPreDraw(SafeAreaProvider.kt:39) at android.view.ViewTreeObserver.dispatchOnPreDraw(ViewTreeObserver.java:1121) at android.view.ViewRootImpl.performTraversals(ViewRootImpl.java:4442) at android.view.ViewRootImpl.doTraversal(ViewRootImpl.java:3116) at android.view.ViewRootImpl$TraversalRunnable.run(ViewRootImpl.java:10885) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1301) at android.view.Choreographer$CallbackRecord.run(Choreographer.java:1309) at android.view.Choreographer.doCallbacks(Choreographer.java:923) at android.view.Choreographer.doFrame(Choreographer.java:852) at android.view.Choreographer$FrameDisplayEventReceiver.run(Choreographer.java:1283) at android.os.Handler.handleCallback(Handler.java:942) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:226) at android.os.Looper.loop(Looper.java:313) at android.app.ActivityThread.main(ActivityThread.java:8762) at java.lang.reflect.Method.invoke(Native Method) at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:604) at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1067)

React Native Version

0.72.6

Output of npx react-native info

System: OS: macOS 13.5.1 CPU: (10) arm64 Apple M1 Pro Memory: 78.66 MB / 16.00 GB Shell: version: 3.2.57 path: /bin/bash Binaries: Node: version: 18.18.2 path: /opt/homebrew/opt/node@18/bin/node Yarn: Not Found npm: version: 7.24.2 path: ???/node_modules/.bin/npm Watchman: version: 2023.07.10.00 path: /opt/homebrew/bin/watchman Managers: CocoaPods: version: 1.12.1 path: /opt/homebrew/bin/pod SDKs: iOS SDK: Platforms: - DriverKit 22.1 - iOS 16.1 - macOS 13.0 - tvOS 16.1 - watchOS 9.1 Android SDK: Not Found IDEs: Android Studio: Not Found Xcode: version: 14.1/14B47b path: /usr/bin/xcodebuild Languages: Java: version: 11.0.17 path: /Library/Java/JavaVirtualMachines/jdk-11.0.17+8/Contents/Home/bin/javac Ruby: version: 2.6.10 path: /usr/bin/ruby npmPackages: "@react-native-community/cli": Not Found react: installed: 18.2.0 wanted: 18.2.0 react-native: installed: 0.72.6 wanted: ^0.72.6 react-native-macos: Not Found npmGlobalPackages: "react-native": Not Found Android: hermesEnabled: false newArchEnabled: false iOS: hermesEnabled: true newArchEnabled: false

Steps to reproduce

  1. Build a debug apk with index.android.bundle
  2. Side load the apk to a Samsung S20 Ultra or Pixel 5
  3. Start the app
  4. Observe if an error screen shows up
  5. If no, delete the app and repeat step 2 to 4

Snack, screenshot, or link to a repository

Screenshot 2023-11-16 at 2 35 39 PM

simonxcheng avatar Nov 16 '23 22:11 simonxcheng

:warning: Newer Version of React Native is Available!
:information_source: You are on a supported minor version, but it looks like there's a newer patch available - 0.72.7. Please upgrade to the highest patch for your minor or latest and verify if the issue persists (alternatively, create a new project and repro the issue in it). If it does not repro, please let us know so we can close out this issue. This helps us ensure we are looking at issues that still exist in the most recent releases.

github-actions[bot] avatar Nov 16 '23 22:11 github-actions[bot]

:warning: Missing Reproducible Example
:information_source: We could not detect a reproducible example in your issue report. Please provide either:
  • If your bug is UI related: a Snack
  • If your bug is build/update related: use our Reproducer Template. A reproducer needs to be in a GitHub repository under your username.

github-actions[bot] avatar Nov 16 '23 22:11 github-actions[bot]

Side load the apk to a Samsung S20 Ultra or Pixel 5

Is this specific to those 2 modules only?

cortinico avatar Nov 22 '23 10:11 cortinico

I'm having the same issue on pixel 6a emulator

tomgreco avatar Nov 22 '23 20:11 tomgreco

Side load the apk to a Samsung S20 Ultra or Pixel 5

Is this specific to those 2 modules only?

We found the same issue on pixel 6 pro, pixel 2 xl, pixel 4a, and Samsung s21

simonxcheng avatar Nov 24 '23 00:11 simonxcheng

I'm having the same problem, and I don't think it is a device issue. Did you find any solutions?

mrcnserkan avatar Jan 17 '24 21:01 mrcnserkan

Side load the apk to a Samsung S20 Ultra or Pixel 5

Is this specific to those 2 modules only?

@cortinico We released our production build with RN 0.72.8. We found that 5% users got this crash with phased rollout. We also found a workaround to reduce the crash percentage to 0.15%. However, it is still too hight for us because our crash-free rate was about 99.95%. I guess the issue is coming from some race conditions could not being handled with RN Framework. It did not happen with RN 0.68. Here are some details. I can not reveal our source code. I am not sure if it necessary to create a separate project to reproduce this issue without my company's source code. Please let me know if you need more information.

  1. Our code is sophisticated. We are using brown field approach to build our app. The home page has a native ViewPager to render 5 tabs. The 1st, 2nd, and 4th are rendered by RN fragements, the 3rd and 5th are rendered in native fragments. We used to set offscreenPageLimit to 4 to keep these fragments in memory. There is no issues with RN 0.68 and ealier versions.
image002
  1. The crash only happens 1~2 seconds after the app starts
  2. The work around we found is to delay loading other fragments. So, when the activity being created, we set offscreenPageLimit to 1. After 100ms, we set offscreenPageLimit to 4. With this tweak, we elimiated 97% crashes.
  3. In debug mode and by setting break points on UIImplementation.java. I did not find registerRootView has been invoked for node 21. How node tag is generated? In RN souce code, allocateTag never generate a tag with value 21.

Callstack: Caused by java.lang.AssertionError: Root node with tag 21 doesn't exist

   at com.facebook.infer.annotation.Assertions.assertNotNull(Assertions.java:19)

   at com.facebook.react.uimanager.UIImplementation.createView(UIImplementation.java:235)

   at com.facebook.react.uimanager.UIManagerModule.createView(UIManagerModule.java:419)

   at java.lang.reflect.Method.invoke(Method.java)

   at com.facebook.react.bridge.JavaMethodWrapper.invoke(JavaMethodWrapper.java:372)

   at com.facebook.react.bridge.JavaModuleWrapper.invoke(JavaModuleWrapper.java:188)

   at com.facebook.jni.NativeRunnable.run(NativeRunnable.java)

   at android.os.Handler.handleCallback(Handler.java:958)

   at android.os.Handler.dispatchMessage(Handler.java:99)

   at com.facebook.react.bridge.queue.MessageQueueThreadHandler.dispatchMessage(MessageQueueThreadHandler.java:27)

   at android.os.Looper.loopOnce(Looper.java:230)

   at android.os.Looper.loop(Looper.java:319)

   at com.facebook.react.bridge.queue.MessageQueueThreadImpl$4.run(MessageQueueThreadImpl.java:228)

   at java.lang.Thread.run(Thread.java:1012)

simonxcheng avatar Feb 06 '24 01:02 simonxcheng

Please let me know if you need more information.

We would need a reproducer as suggested by the bot

cortinico avatar Feb 06 '24 11:02 cortinico

import static android.content.Context.VIBRATOR_SERVICE;

import android.app.Activity;
import android.app.PendingIntent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Build;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.github.devnied.emvnfccard.enums.EmvCardScheme;
import com.github.devnied.emvnfccard.model.Application;
import com.github.devnied.emvnfccard.model.EmvCard;
import com.github.devnied.emvnfccard.parser.EmvTemplate;

import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;

public class NativeModule extends ReactContextBaseJavaModule implements NfcAdapter.ReaderCallback {
    /**
     * Name of component so it can be accessed in JS
     */
    public static final String REACT_CLASS = "NativeModule";
    private Activity activity;
    private NfcAdapter mNfcAdapter;
    private Promise readNfcPromise;

    NativeModule(ReactApplicationContext context) {
        super(context);
        activity = context.getCurrentActivity();

        // For NFC reading
        mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
    }

    @Override
    public String getName() {
        return REACT_CLASS;
    }


    @ReactMethod
    public void readNfc(Promise promise) {
        if(promise != null) {
            if(readNfcPromise != null) {
                promise.reject("Already reading", "Already reading");
                return;
            }
            if (mNfcAdapter != null) {
                this.readNfcPromise = promise;
                Bundle options = new Bundle();
                // Work around for some broken Nfc firmware implementations that poll the card too fast
                options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);

                // Enable ReaderMode for all types of card and disable platform sounds
                // the option NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK is NOT set
                // to get the data of the tag after reading
                mNfcAdapter.enableReaderMode(this.activity,
                        this,
                        NfcAdapter.FLAG_READER_NFC_A |
                                NfcAdapter.FLAG_READER_NFC_B |
                                NfcAdapter.FLAG_READER_NFC_F |
                                NfcAdapter.FLAG_READER_NFC_V |
                                NfcAdapter.FLAG_READER_NFC_BARCODE |
                                NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
                        options);
            } else {
                promise.reject("NFC reader not initialized", "NFC reader not initialized");
                return;
            }
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onTagDiscovered(Tag tag) {
    }
}

I recently added NFC reading to my native module and I am getting this error now. It's related to this line mNfcAdapter = NfcAdapter.getDefaultAdapter(context);

If I comment that line out I don't get the error, but then nothing works because I need that line :)

tgreco avatar Feb 07 '24 18:02 tgreco

Please let me know if you need more information.

We would need a reproducer as suggested by the bot

I believe I found out why our app is carshing. We use the following code in our RN Fragment to update props when there are some data changes. If a such change occurs during the initialization of the RN App inside the RN Fragment, the error happens:

reactRootView?.appProperties = newProps

The only fix we can apply right now is to deplay the appProperties update. We will try to use DeviceEventEmitter to update data from Native to RN to see if we can remove the issue completely.

simonxcheng avatar Feb 09 '24 00:02 simonxcheng

import static android.content.Context.VIBRATOR_SERVICE;

import android.app.Activity;
import android.app.PendingIntent;
import android.nfc.NfcAdapter;
import android.nfc.Tag;
import android.nfc.tech.IsoDep;
import android.os.Build;
import android.os.Bundle;
import android.os.VibrationEffect;
import android.os.Vibrator;

import androidx.annotation.NonNull;
import androidx.annotation.RequiresApi;

import com.facebook.react.bridge.Arguments;
import com.facebook.react.bridge.Promise;
import com.facebook.react.bridge.ReactApplicationContext;
import com.facebook.react.bridge.ReactContextBaseJavaModule;
import com.facebook.react.bridge.ReactMethod;
import com.facebook.react.bridge.WritableMap;
import com.github.devnied.emvnfccard.enums.EmvCardScheme;
import com.github.devnied.emvnfccard.model.Application;
import com.github.devnied.emvnfccard.model.EmvCard;
import com.github.devnied.emvnfccard.parser.EmvTemplate;

import java.io.IOException;
import java.time.LocalDate;
import java.time.ZoneId;
import java.util.Date;
import java.util.List;

public class NativeModule extends ReactContextBaseJavaModule implements NfcAdapter.ReaderCallback {
    /**
     * Name of component so it can be accessed in JS
     */
    public static final String REACT_CLASS = "NativeModule";
    private Activity activity;
    private NfcAdapter mNfcAdapter;
    private Promise readNfcPromise;

    NativeModule(ReactApplicationContext context) {
        super(context);
        activity = context.getCurrentActivity();

        // For NFC reading
        mNfcAdapter = NfcAdapter.getDefaultAdapter(context);
    }

    @Override
    public String getName() {
        return REACT_CLASS;
    }


    @ReactMethod
    public void readNfc(Promise promise) {
        if(promise != null) {
            if(readNfcPromise != null) {
                promise.reject("Already reading", "Already reading");
                return;
            }
            if (mNfcAdapter != null) {
                this.readNfcPromise = promise;
                Bundle options = new Bundle();
                // Work around for some broken Nfc firmware implementations that poll the card too fast
                options.putInt(NfcAdapter.EXTRA_READER_PRESENCE_CHECK_DELAY, 250);

                // Enable ReaderMode for all types of card and disable platform sounds
                // the option NfcAdapter.FLAG_READER_SKIP_NDEF_CHECK is NOT set
                // to get the data of the tag after reading
                mNfcAdapter.enableReaderMode(this.activity,
                        this,
                        NfcAdapter.FLAG_READER_NFC_A |
                                NfcAdapter.FLAG_READER_NFC_B |
                                NfcAdapter.FLAG_READER_NFC_F |
                                NfcAdapter.FLAG_READER_NFC_V |
                                NfcAdapter.FLAG_READER_NFC_BARCODE |
                                NfcAdapter.FLAG_READER_NO_PLATFORM_SOUNDS,
                        options);
            } else {
                promise.reject("NFC reader not initialized", "NFC reader not initialized");
                return;
            }
        }
    }

    @RequiresApi(api = Build.VERSION_CODES.O)
    @Override
    public void onTagDiscovered(Tag tag) {
    }
}

I recently added NFC reading to my native module and I am getting this error now. It's related to this line mNfcAdapter = NfcAdapter.getDefaultAdapter(context);

If I comment that line out I don't get the error, but then nothing works because I need that line :) @tgreco Does NfcAdapter trigger any props update of your RN components? From my experience, props change causes this issue if the component is still loading/initialzing

simonxcheng avatar Feb 09 '24 00:02 simonxcheng

The way I solved this was by moving mNfcAdapter = NfcAdapter.getDefaultAdapter(context); into a method that I would call from react-native along with using getReactApplicationContext() instead of the context that was passed in the constructor.

Now it is working fine.

tgreco avatar Feb 09 '24 15:02 tgreco

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

react-native-bot avatar Aug 08 '24 05:08 react-native-bot

This issue is stale because it has been open 180 days with no activity. Remove stale label or comment or this will be closed in 7 days.

react-native-bot avatar Aug 08 '24 05:08 react-native-bot

This issue was closed because it has been stalled for 7 days with no activity.

react-native-bot avatar Aug 15 '24 05:08 react-native-bot