Flutterfire: [cloud_firestore] DoTransaction failed: Attempt to invoke virtual method

Created on 12 Sep 2019  路  7Comments  路  Source: FirebaseExtended/flutterfire

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...
customer cloud_firestore bug

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...

All 7 comments

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

Was this page helpful?
0 / 5 - 0 ratings