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:
- The app starts and fetches its configuration from our backend. This configuration includes the full path string for the Realtime Database listener.
- This configuration object is saved locally to the device's storage using Hive for caching.
- Later, a background service reads this path from the local cache to initialize a Realtime Database listener.
- The service calls
FirebaseDatabase.instance.ref(path).onValue.listen(...)
. - 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