Skip to content

[firebase_database] Native Android crash: IllegalStateException - Loading an unrelated row with path #7082

Open
@rajaghaneshanp-plateron

Description

What happened?

Our application is experiencing a fatal native crash on Android with the message java.lang.IllegalStateException: Load an unrelated row.

After careful analysis of the crash logs, we've discovered that the root cause appears to be a data corruption issue within a Firebase Realtime Database path. Specifically, two characters, 23, are being replaced by a single space character ( ).

This corruption leads to a mismatch between the path the SDK expects and the path it finds in its local cache, triggering the IllegalStateException.

  • Original Path Segment (Expected):
    .../b323a675-2af4-4c15-9ed2-xxxx_notifications/...

  • Corrupted Path Segment (From Crash Log):
    .../b3 a675-2af4-4c15-9ed2-xxxx_notifications/...

We have verified with our backend team that the correct, uncorrupted path is being sent to the client. This strongly suggests the corruption is happening on the client side, possibly within the native Android SDK's persistence or caching layer.

Steps to reproduce

The issue is difficult to reproduce consistently as it has only been observed in our production environment. However, the logical flow that leads to the crash is as follows:

  1. The app starts and fetches its configuration from our backend. This configuration includes the full path string for the Realtime Database listener.
  2. This configuration object is saved locally to the device's storage using Hive for caching.
  3. Later, a background service reads this path from the local cache to initialize a Realtime Database listener.
  4. The service calls FirebaseDatabase.instance.ref(path).onValue.listen(...).
  5. At this point, the native Android SDK throws the IllegalStateException, and the application crashes.

Relevant Code

While we cannot provide a minimal, reproducible example due to the complexity and intermittent nature of the bug, the conceptual code that triggers the crash is as follows:

// This code runs in a service that initializes our notification listeners.
// The `subscribeUrl` string is retrieved from a settings object that was cached locally.
// At the time of the crash, this variable holds the corrupted path.

final String corruptedSubscribeUrl = '.../b3 a675-2af4-4c15-9ed2-92d4de7aa0d0_notifications/...';

try {
  // This is the call that ultimately triggers the native crash.
  // The Dart-level try/catch does not prevent the fatal native exception.
  final databaseReference = FirebaseDatabase.instance.ref(corruptedSubscribeUrl);
  databaseReference.onValue.listen((event) {
    // Handle incoming data.
  });
} catch (e, s) {
  // This exception is not a Dart exception and is not caught here.
  // The app crashes before this point.
}

Flutter dependencies

# pubspec.yaml
  firebase_core: ^3.13.1
  firebase_crashlytics: ^4.3.6
  firebase_database: ^11.3.6
  cloud_firestore: ^5.6.8
  firebase_remote_config: ^5.4.4
  firebase_analytics: ^11.4.6

I didn't have firebase BoM dependency in build.gradle

Full Crash Log Fatal Exception: java.lang.IllegalStateException: Load an unrelated row with path /pos_notifications_prod/listeners/b3 a675-2af4-4c15-9ed2-xxxx_notifications/8e9f42ed-39df-42de-aa2a-xxxx_pos_terminal/51c17f67-7dc0-4fbb-876d-xxxx_message and query /pos_notifications_prod/listeners/b323a675-2af4-4c15-9ed2-xxxx_notifications/8e9f42ed-39df-42de-aa2a-xxxx_pos_terminal at com.google.firebase.database.core.persistence.DefaultPersistenceManager.serverCache(DefaultPersistenceManager.java:232) at com.google.firebase.database.core.SyncTree.serverCache(SyncTree.java:319) at com.google.firebase.database.core.SyncTree.collectServerChildren(SyncTree.java:311) at com.google.firebase.database.core.SyncTree.access$500(SyncTree.java:36) at com.google.firebase.database.core.SyncTree$ListenProvider$1.onComplete(SyncTree.java:541) at com.google.firebase.database.core.persistence.DefaultPersistenceManager.runInsideTransaction(DefaultPersistenceManager.java:436) at com.google.firebase.database.core.SyncTree$ListenProvider.startListening(SyncTree.java:515) at com.google.firebase.database.core.SyncTree.addEventRegistration(SyncTree.java:94) at com.google.firebase.database.core.Repo.addEventCallback(Repo.java:214) at com.google.firebase.database.DatabaseReference.addValueEventListener(DatabaseReference.java:189) at com.google.firebase.database.DatabaseReference.setValue(DatabaseReference.java:119) at io.flutter.plugins.firebase.database.MethodCallHandlerImpl.onMethodCall(MethodCallHandlerImpl.java:323) at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler.onMessage(MethodChannel.java:267) at io.flutter.embedding.engine.dart.DartMessenger.invokeHandler(DartMessenger.java:295) at io.flutter.embedding.engine.dart.DartMessenger.lambda$dispatchMessageToQueue$0$io-flutter-embedding-engine-dart-DartMessenger(DartMessenger.java:322) at io.flutter.embedding.engine.dart.DartMessenger$$ExternalSyntheticLambda0.run(DartMessenger.java) at android.os.Handler.handleCallback(Handler.java:958) at android.os.Handler.dispatchMessage(Handler.java:99) at android.os.Looper.loopOnce(Looper.java:205) at android.os.Looper.loop(Looper.java:294) at android.os.HandlerThread.run(HandlerThread.java:67)

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions