Skip to content

[pigeon] Native interop (JNI) calls fail with ClassNotFoundException in minified Android release builds #193880

Closed
flutter/packages
#13151

Description

@tarrinneal

Found while investigating #193195.

Summary

Flutter's Gradle plugin enables R8 for Android release builds by default. R8 renames or removes classes that pigeon's JNI native interop looks up by name, so most native interop calls fail in a default release build. Debug and profile builds aren't affected, which makes this easy to miss until shipping.

Steps to reproduce

  1. Generate a pigeon file with kotlin_use_jni (for example pigeon's own pigeons/native_interop_tests.dart).
  2. Build the app in release mode with default settings (flutter build apk --release or flutter run --release).
  3. Call native interop methods that take or return a data class or an enum, are @async, throw an error, or go through a FlutterApi.

Expected results

All calls behave as in debug builds.

Actual results

In a release build of pigeon's test plugin, 15 of 25 probe calls fail with ClassNotFoundException.

  • Pass: getInstance, noop, and echoes of int, double, bool, String, Uint8List, List, List<int> and Map.
  • Fail: everything that touches an enum, a data class, an async method, an error, or the FlutterApi.
  • 14 of the 15 failures name the generated error class instead of the class that actually went missing. The generated error handling looks the error class up by name too, and that lookup fails first, which hides the original failure.

Cause

mapping.txt from the release build shows R8:

  • renamed the data classes and enums (for example NativeInteropAllTypes → eg);
  • removed the FlutterApi interface and its Registrar;
  • inlined the generated error class away;
  • renamed kotlin.Unit, kotlin.Result, kotlin.Result$Failure, kotlin.coroutines.Continuation and kotlin.coroutines.intrinsics.CoroutineSingletons, which package:jni and the generated Dart look up by name.

Only @Keep classes survived intact. The generated Kotlin has exactly three @Keep annotations: the HostApi interface (L1005), its Registrar (L1625) and getInstance (L1642). package:jni's consumer rules keep only its own package.

Re-running with the generated classes kept, plus six Kotlin classes (the five above and kotlin.coroutines.intrinsics.IntrinsicsKt), made all 25 calls pass. A macOS release build using FFI also passed all 25.

Workaround

Add keep rules to the plugin's consumer ProGuard file (wired up with consumerProguardFiles in the plugin's build.gradle), so that apps pick them up automatically. Replace the first rule with the package of your generated Kotlin file, or list the generated classes one by one:

# Classes generated by pigeon with kotlin_use_jni.
-keep class com.example.my_plugin.** { *; }

# Kotlin classes that package:jni and the generated Dart look up by name.
-keep class kotlin.Unit { *; }
-keep class kotlin.Result { *; }
-keep class kotlin.Result$Failure { *; }
-keep class kotlin.coroutines.Continuation { *; }
-keep class kotlin.coroutines.intrinsics.CoroutineSingletons { *; }
-keep class kotlin.coroutines.intrinsics.IntrinsicsKt { *; }

Proposed fix

  1. Pigeon: add @Keep to every generated type that JNI reaches by name: data classes, enums, the FlutterApi interface and its Registrar, and the error class. A partial fix that keeps only the error class is in progress. It makes the real failures visible, but doesn't fix them on its own.
  2. package:jni: ship consumer rules for the Kotlin classes it looks up by name. This needs an upstream issue in dart-lang/native. Until it lands, pigeon's docs should list the rules above.
  3. CI: run pigeon's native interop integration tests against a minified release build, so regressions show up before release.

Environment

pigeon 29.0.6, package:jni 1.1.0, jnigen 1.0.1, Flutter 3.49.0-1.0.pre-194, JDK 17, arm64 Android emulator (Pixel 9 Pro image).

Activity

  1. tarrinneal commented on Oct 6, 2026

    @tarrinneal
    ContributorAuthor
  2. self-assigned this
    on Oct 6, 2026
  3. added
    packageflutter/packages repository. See also p: labels.
    P2Important issues not at the top of the work list
    on Oct 6, 2026
  4. github-actions commented on Oct 21, 2026

    @github-actions

    This thread has been automatically locked since there has not been any recent activity after it was closed. If you are still experiencing a similar issue, please open a new bug, including the output of flutter doctor -v and a minimal reproduction of the issue.

  5. locked as resolved and limited conversation to collaborators on Oct 21, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

P2Important issues not at the top of the work listp: pigeonrelated to pigeon messaging codegen toolpackageflutter/packages repository. See also p: labels.team-ecosystemOwned by Ecosystem teamtriaged-ecosystemTriaged by Ecosystem team

Type

No type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions