I am seeing this exception often, presumably when multiple transactions on the same document are queued:
E/CloudFirestorePlugin(28589): java.lang.Exception: DoTransaction failed: Attempt to invoke virtual method 'com.google.firebase.firestore.DocumentSnapshot com.google.firebase.firestore.Transaction.get(com.google.firebase.firestore.DocumentReference)' on a null object reference
E/CloudFirestorePlugin(28589): java.util.concurrent.ExecutionException: java.lang.Exception: DoTransaction failed: Attempt to invoke virtual method 'com.google.firebase.firestore.DocumentSnapshot com.google.firebase.firestore.Transaction.get(com.google.firebase.firestore.DocumentReference)' on a null object reference
E/CloudFirestorePlugin(28589): at com.google.android.gms.tasks.Tasks.zzb(Unknown Source:61)
E/CloudFirestorePlugin(28589): at com.google.android.gms.tasks.Tasks.await(Unknown Source:33)
E/CloudFirestorePlugin(28589): at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$4.apply(CloudFirestorePlugin.java:424)
E/CloudFirestorePlugin(28589): at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$4.apply(CloudFirestorePlugin.java:376)
E/CloudFirestorePlugin(28589): at com.google.firebase.firestore.FirebaseFirestore.lambda$runTransaction$1(com.google.firebase:firebase-firestore@@19.0.0:283)
E/CloudFirestorePlugin(28589): at com.google.firebase.firestore.FirebaseFirestore$$Lambda$3.call(Unknown Source:6)
E/CloudFirestorePlugin(28589): at com.google.android.gms.tasks.zzv.run(Unknown Source:2)
E/CloudFirestorePlugin(28589): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
E/CloudFirestorePlugin(28589): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
E/CloudFirestorePlugin(28589): at java.lang.Thread.run(Thread.java:764)
E/CloudFirestorePlugin(28589): Caused by: java.lang.Exception: DoTransaction failed: Attempt to invoke virtual method 'com.google.firebase.firestore.DocumentSnapshot com.google.firebase.firestore.Transaction.get(com.google.firebase.firestore.DocumentReference)' on a null object reference
E/CloudFirestorePlugin(28589): at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$4$1$1.error(CloudFirestorePlugin.java:406)
E/CloudFirestorePlugin(28589): at io.flutter.plugin.common.MethodChannel$IncomingResultHandler.reply(MethodChannel.java:201)
E/CloudFirestorePlugin(28589): at io.flutter.embedding.engine.dart.DartMessenger.handlePlatformMessageResponse(DartMessenger.java:114)
E/CloudFirestorePlugin(28589): at io.flutter.embedding.engine.FlutterJNI.handlePlatformMessageResponse(FlutterJNI.java:655)
E/CloudFirestorePlugin(28589): at android.os.MessageQueue.nativePollOnce(Native Method)
E/CloudFirestorePlugin(28589): at android.os.MessageQueue.next(MessageQueue.java:325)
E/CloudFirestorePlugin(28589): at android.os.Looper.loop(Looper.java:142)
E/CloudFirestorePlugin(28589): at android.app.ActivityThread.main(ActivityThread.java:6983)
E/CloudFirestorePlugin(28589): at java.lang.reflect.Method.invoke(Native Method)
E/CloudFirestorePlugin(28589): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:441)
E/CloudFirestorePlugin(28589): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
D/AndroidRuntime(28589): Shutting down VM
E/AndroidRuntime(28589): FATAL EXCEPTION: main
E/AndroidRuntime(28589): java.lang.IllegalStateException: Reply already submitted
E/AndroidRuntime(28589): at io.flutter.embedding.engine.dart.DartMessenger$Reply.reply(DartMessenger.java:151)
E/AndroidRuntime(28589): at io.flutter.plugin.common.MethodChannel$IncomingMethodCallHandler$1.error(MethodChannel.java:230)
E/AndroidRuntime(28589): at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$3.onComplete(CloudFirestorePlugin.java:442)
E/AndroidRuntime(28589): at com.google.android.gms.tasks.zzj.run(Unknown Source:4)
E/AndroidRuntime(28589): at android.os.Handler.handleCallback(Handler.java:790)
E/AndroidRuntime(28589): at android.os.Handler.dispatchMessage(Handler.java:99)
E/AndroidRuntime(28589): at android.os.Looper.loop(Looper.java:164)
E/AndroidRuntime(28589): at android.app.ActivityThread.main(ActivityThread.java:6983)
E/AndroidRuntime(28589): at java.lang.reflect.Method.invoke(Native Method)
E/AndroidRuntime(28589): at com.android.internal.os.RuntimeInit$MethodAndArgsCaller.run(RuntimeInit.java:441)
E/AndroidRuntime(28589): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:1408)
E/CloudFirestorePlugin(28589): Timed out waiting for Task
E/CloudFirestorePlugin(28589): java.util.concurrent.TimeoutException: Timed out waiting for Task
E/CloudFirestorePlugin(28589): at com.google.android.gms.tasks.Tasks.await(Unknown Source:32)
E/CloudFirestorePlugin(28589): at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$4.apply(CloudFirestorePlugin.java:424)
E/CloudFirestorePlugin(28589): at io.flutter.plugins.firebase.cloudfirestore.CloudFirestorePlugin$4.apply(CloudFirestorePlugin.java:376)
E/CloudFirestorePlugin(28589): at com.google.firebase.firestore.FirebaseFirestore.lambda$runTransaction$1(com.google.firebase:firebase-firestore@@19.0.0:283)
E/CloudFirestorePlugin(28589): at com.google.firebase.firestore.FirebaseFirestore$$Lambda$3.call(Unknown Source:6)
E/CloudFirestorePlugin(28589): at com.google.android.gms.tasks.zzv.run(Unknown Source:2)
E/CloudFirestorePlugin(28589): at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1162)
E/CloudFirestorePlugin(28589): at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:636)
E/CloudFirestorePlugin(28589): at java.lang.Thread.run(Thread.java:764)`
Unfortunately this is a fatal exception which shuts down the VM and crashes the app...
have you solved this issue !?
I didn't solve it, I just implemented a work-around. Specifically, when a transaction starts, if its DocumentReference is already being used in a running transaction, it waits for it to complete. This allows concurrency between non-matching DocumentReferences, but queues transactions when the DocumentReference matches.
I didn't solve it, I just implemented a work-around. Specifically, when a transaction starts, if its DocumentReference is already being used in a running transaction, it waits for it to complete. This allows concurrency between non-matching DocumentReferences, but queues transactions when the DocumentReference matches.
And how do you do that in code?
This is a stripped down version of what I was referring to. You can do more to this such as make it static or singleton based on your needs. Essentially it just routes transactions through a service which prevents concurrency on matching DocumentReference paths. It requires a function that merges whats currently at that location and what is passed. I have not tested this stripped down version, but it should give you an idea.
import 'dart:async';
import 'package:cloud_firestore/cloud_firestore.dart';
class Document
{
final Map<String, dynamic> data;
final DocumentReference reference;
const Document(this.reference, this.data) :
assert(reference != null),
assert(data != null);
}
class Database
{
final Map<String, Completer<bool>> _transactions = {};
Future<bool> transact(Document document, Document Function(Document data, Document newData) merge) async
{
if(document == null || merge == null)
return false;
if(_transactions.containsKey(document.reference.path))
await _transactions[document.reference.path].future;
final Completer<bool> result = Completer<bool>();
_transactions[document.reference.path] = result;
try
{
await Firestore.instance.runTransaction((Transaction transaction) async
{
try
{
final DocumentSnapshot snapshot = await transaction.get(document.reference);
final Document data = snapshot != null && snapshot.exists && snapshot.data != null ? Document(document.reference, snapshot.data) : null;
final Document merged = merge(data, document);
if(merged != null)
await transaction.set(document.reference, merged.data);
_onTransact(document.reference.path, merged != null);
}
catch(error) {
_onTransact(document.reference.path, false); }
});
}
catch(error) {
_onTransact(document.reference.path, false); }
return result.future;
}
void _onTransact(String path, bool result)
{
if(path == null || !_transactions.containsKey(path) || result == null)
return;
_transactions[path].complete(result);
_transactions.remove(path);
}
}
I'm having the error: Unhandled Exception: PlatformException(Error performing transaction, Attempt to invoke virtual method 'void android.app.Activity.runOnUiThread(java.lang.Runnable)' on a null object reference, null)
is it the same?
It looks similar. For what it's worth - we moved away from using transactions to regular gets/sets/updates since transactions have numerous issues, anything that couldn't be transitioned went into cloud functions...
Hey all, as part of our on-going work for #2582, this has been resolved in our Firebase Firestore rework (#2913) (transactions were completely reworked) - which has now been merged into master. We'll look at publishing some prereleases in the next few days. Thank you
Most helpful comment
It looks similar. For what it's worth - we moved away from using transactions to regular gets/sets/updates since transactions have numerous issues, anything that couldn't be transitioned went into cloud functions...