Skip to content

Android release builds crash on launch: react-native-shiki-engine resolves fbjni:+ to 0.8.1 #13710

Description

@bompus

Fresh Android builds of the mobile app now exit immediately on launch. logcat:

UnsatisfiedLinkError: dlopen failed: cannot locate symbol "__cxa_init_primary_exception"
referenced by ".../base.apk!/lib/arm64-v8a/libfbjni.so"
  at com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsCxxInterop.<clinit>
  at com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative
  at ...MainApplication.onCreate(MainApplication.kt:43)

react-native-shiki-engine@0.3.12 declares implementation "com.facebook.fbjni:fbjni:+". fbjni 0.8.1 was published to Maven Central on 2026-09-25, so Gradle resolves the conflict with RN's fbjni:0.7.0 up to 0.8.1 (gradlew :app:dependencyInsight --dependency com.facebook.fbjni:fbjni). That libfbjni.so is built with the NDK 28 toolchain (clang 19). RN 0.86 builds with NDK 27.1 and packages that libc++_shared.so, which lacks the symbol.

Builds with fbjni 0.7.0 already in the Gradle cache may not reproduce this.

Workaround: force RN's version in android/build.gradle:

allprojects {
  configurations.all {
    resolutionStrategy.force 'com.facebook.fbjni:fbjni:0.7.0'
  }
}

A permanent fix could apply this through the app's Expo config plugin, or pin the version in react-native-shiki-engine.

Seen on v0.0.43-nightly.20260925.2269 (4293433e), Pixel 11 Pro XL, arm64-v8a.

Activity

  1. juliusmarminge commented on Sep 25, 2026

    @juliusmarminge
    Member

    Triage

    Confirmed. The report matches current main (ed809f7ad2) and the nightly it names (4293433e, which is an ancestor of main). Nothing since that commit touches the mobile Android build or react-native-shiki-engine. No other issue or pull request in this repo tracks it. Keep this open.

    A fresh Android resolve today links libfbjni.so from fbjni 0.8.1 into an app whose C++ runtime is still React Native’s NDK 27.1 libc++_shared.so. The process dies in ReactNativeApplicationEntryPoint.loadReactNative before any JavaScript runs. Builds that already resolved fbjni:+ to 0.7.0 stay on that copy until the dynamic-version cache expires, which is why a warm Gradle cache does not reproduce it.

    What the report gets right

    fbjni 0.8.0 and 0.8.1 were both published on 2026-09-25 (0.8.0 at 16:04 UTC, 0.8.1 at 17:36 UTC). Both POMs are on Maven Central. 0.8.0 is the NDK r28c rebuild, done so fbjni would be ready for facebook/react-native#58650. That pull request is still open. 0.8.1 only restores the Prefab package name from fbjni-android back to fbjni. + therefore selects 0.8.1, and that .so is the r28c binary.

    React Native 0.86.3 does not. packages/react-native/gradle/libs.versions.toml on tag v0.86.3 sets fbjni = "0.7.0" and ndkVersion = "27.1.12297006". The published Gradle module for com.facebook.react:react-android:0.86.3 requires com.facebook.fbjni:fbjni:0.7.0 on the release runtime variant. The React Native Gradle plugin forces react-android and hermes-android only. It does not force fbjni, so the highest request wins.

    This app is that pair of versions, and the floating request is react-native-shiki-engine@0.3.12:

        "react-native": "0.86.3",
        ...
        "react-native-shiki-engine": "^0.3.12",

    android/build.gradle in that package, on tag v0.3.12 and on the library’s current main, still has implementation "com.facebook.fbjni:fbjni:+". The lockfile is exactly 0.3.12. npm has no newer release. The library’s own skiniks/react-native-shiki-engine#273, filed by the same author, is still open.

    The other Android libraries checked do not make this request. react-native-screens, react-native-gesture-handler, react-native-nitro-modules, and expo-modules-core exclude libfbjni.so from their own AARs (gesture-handler also excludes the Maven group). expo-modules-core only compileOnlys fbjni 0.5.1. react-native-nitro-markdown, react-native-keyboard-controller, react-native-svg, react-native-webview, and @react-native-menu/menu do not declare fbjni. None of the in-repo Android modules do either.

    The launch stack does not require the highlighter to run. Autolinking compiles the library in because it is a dependency. The JS import is dynamic and only used once a review diff is highlighted:

    async function createNativeReviewDiffHighlighter(): Promise<NativeReviewDiffHighlighterHandle> {
      const nativeEngineModule = await import("react-native-shiki-engine");
      if (!nativeEngineModule.isNativeEngineAvailable()) {

    ReactNativeFeatureFlagsCxxInterop loads libfbjni.so from MainApplication.onCreate. The missing symbol is __cxa_init_primary_exception, which NDK r28’s libc++ references and NDK r27’s libc++_shared.so does not provide. That is the same break as facebook/react-native#54886.

    What to be careful with

    Editing android/build.gradle by hand does not survive this repo. apps/mobile/android is generated and gitignored, and the Android scripts run expo prebuild --clean:

    # generated native folders
    /ios
    /android

    EAS builds prebuild from app.config.ts too. The force has to be applied from a config plugin, the same way withAndroidGradleHeap is registered here:

        "./plugins/withAndroidCleartextTraffic.cjs",
        "./plugins/withAndroidGradleHeap.cjs",

    gradle.properties cannot express it. Use withProjectBuildGradle and put resolutionStrategy.force 'com.facebook.fbjni:fbjni:0.7.0' on allprojects.configurations. 0.7.0 is the version react-android:0.86.3 requires, not a permanent pin. When this app picks up the NDK r28c bump from react-native#58650, that force has to move with React Native’s fbjni or the mismatch flips the other way.

    This is not release-specific. Debug and release share the resolution. “Release” in the report is the build that resolved cleanly after 0.8.1 existed. An APK already built against 0.7.0 is fine. An Expo update cannot replace libfbjni.so. The nightly named in the report stays broken until a new Android binary is built.

    Workaround

    Until that plugin is in a build, a clean Android build needs the force injected into the generated root android/build.gradle after prebuild. A warm cache that still has + resolved to 0.7.0 will keep launching, and it will stop doing that within Gradle’s dynamic-version window (24 hours) or on the next --refresh-dependencies.

    Labels / next

    • Type: bug, accepted.
    • Labels: bug, accepted. Leave needs-triage, via-triage, and upstream off. The library bug is Cannot dismiss the plan overlay after agent calls the planning tool #273; this app can ship the pin without waiting for it.
    • Next: add the config plugin above and register it beside withAndroidGradleHeap. The pull request changes native bytes, so it should take the native-change label. A pnpm patch that rewrites shiki-engine’s fbjni:+ to 0.7.0 is the narrower alternative and goes stale on the next library release that still uses +.
  2. added
    bugSomething is broken or behaving incorrectly.
    acceptedfeature request accepted
    via-triageFiled through npx t3 triage
    on Sep 25, 2026
  3. j-gaertig commented on Sep 27, 2026

    @j-gaertig

    I can confirm both the diagnosis and the fix on a Galaxy A55 (arm64-v8a, preview APK).

    Problem

    Same crash site as reported (MainApplication.onCreate:43 to DefaultNewArchitectureEntryPoint.load to ReactNativeFeatureFlagsCxxInterop.<clinit>). On my device logcat shows it as a SoLoader lookup failure rather than the UnsatisfiedLinkError in the issue body, but same library and same call chain:

    E AndroidRuntime: com.facebook.soloader.SoLoaderDSONotFoundError: couldn't find DSO to load: libfbjni.so
        at com.facebook.soloader.SoLoader.doLoadLibraryBySoName(SoLoader.java:1216)
        ...
        at com.facebook.react.internal.featureflags.ReactNativeFeatureFlagsCxxInterop.<clinit>(ReactNativeFeatureFlagsCxxInterop.kt:28)
        at com.facebook.react.defaults.DefaultNewArchitectureEntryPoint.load(DefaultNewArchitectureEntryPoint.kt:101)
        at com.facebook.react.ReactNativeApplicationEntryPoint.loadReactNative(ReactNativeApplicationEntryPoint.java:31)
        at com.t3tools.t3code.preview.MainApplication.onCreate(MainApplication.kt:43)
    

    This is commit-independent: I bisected 126 commits and every build crashed identically, including a build differing from a known-good one by a single web-only file. Binary forensics back your fbjni theory: libfbjni.so in my last working APK (built Sep 24) is clang 18 (NDK 27), in the crashing APK (built Sep 27) it is clang 19 (NDK 28).

    Fix

    Your workaround holds: with resolutionStrategy.force 'com.facebook.fbjni:fbjni:0.7.0' in android/build.gradle, dependencyInsight reports com.facebook.fbjni:fbjni:0.7.0 (forced) and fbjni:+ -> 0.7.0, and the packaged libfbjni.so is back to clang 18.

    Validation

    • gradlew :app:dependencyInsight shows 0.7.0 forced, no 0.8.1 in the graph.
    • Artifact check on the built APK: libfbjni.so reports clang 18 again.
    • Installed the fixed build on the A55: the app opens normally, no more white screen crash.
  4. KaKi87 commented on Sep 27, 2026

    @KaKi87

    Hi,
    Where did y'all download preview APKs ?

  5. j-gaertig commented on Sep 27, 2026

    @j-gaertig

    I have my agent build the apk for me every few days. I can send you my skill and script for it, though they are configured for my specific setup.

  6. KaKi87 commented on Sep 27, 2026

    @KaKi87

    Oh okay, well I actually did the same thing but with CI (and also had AI fix this issue) : https://github.com/VibedByKaKi/t3-code-android-nightly

    I was just hoping you somehow found some officially supported APKs

  7. added 2 commits that reference this issue on Sep 27, 2026
    9d32063
    3f8f7dc
  8. kvnloo commented on Sep 29, 2026

    @kvnloo
    Contributor

    I put the app-side guard in #13967: it forces com.facebook.fbjni:fbjni:0.7.0 through an Expo config plugin so clean prebuilds/EAS builds cannot float to 0.8.x again. Kept it narrow: no React Native upgrade and no unrelated Gradle changes.

    I’m leaving that PR draft until the native verification is complete (fresh dependency resolution, release APK launch, and artifact check), since this one fails before JS and the device/build proof matters more than CI-only coverage.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    acceptedfeature request acceptedbugSomething is broken or behaving incorrectly.via-triageFiled through npx t3 triage

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions