Skip to content

Realtime Database Android: keepSynced(true) followed by get() causes AssertionError "listen() called twice for same QuerySpec" #8433

Open

Description

@chodnagho

[READ] Step 1: Are you in the right place?

Yes. I believe this is a bug in the Firebase Realtime Database Android SDK.

[REQUIRED] Step 2: Describe your environment

  • Android Studio version: Android Studio Quail 2
  • Firebase Component: Realtime Database
  • Component version: 22.0.1 (Firebase Android BoM 34.16.0)
  • Persistence: Disabled
  • Platform: Android
  • Language: Java

[REQUIRED] Step 3: Describe the problem

Steps to reproduce:

Calling get() immediately after keepSynced(true) on the same DatabaseReference consistently crashes with:

java.lang.AssertionError:
hardAssert failed: listen() called twice for same QuerySpec

The stack trace points to Firebase Realtime Database internals, including:

SyncTree
Repo.keepSynced
PersistentConnectionImpl.listen

Steps to reproduce:

  1. Create a DatabaseReference.
  2. Call keepSynced(true).
  3. Call get() on the same reference.

Expected behavior:

get() should return the current data without crashing.

Actual behavior:

The application crashes with the assertion above.

I can consistently reproduce this on multiple database paths and in different parts of my application. The common factor is always:

keepSynced(true)
followed by
get()

Replacing get() with addListenerForSingleValueEvent() consistently avoids the crash.

There is also a related Stack Overflow discussion:

https://stackoverflow.com/questions/79981289/firebase-realtime-database-android-keepsyncedtrue-get-causes-listen-called-twice-for-same-queryspec

In that discussion, Frank van Puffelen (former Firebase team member) commented:

"Definitely a bug..."

He also explained that get() likely does not correctly check the SyncTree before deciding to fetch data when keepSynced(true) is already active.

Could the Firebase Realtime Database Android SDK team please confirm whether this is a known issue and whether a fix is planned?

Relevant Code:

DatabaseReference ref = FirebaseDatabase.getInstance()
        .getReference("test/path");

ref.keepSynced(true);

ref.get().addOnCompleteListener(task -> {
    // Read data
});

Working alternative:

DatabaseReference ref = FirebaseDatabase.getInstance()
        .getReference("test/path");

ref.keepSynced(true);

ref.addListenerForSingleValueEvent(new ValueEventListener() {
    @Override
    public void onDataChange(@NonNull DataSnapshot snapshot) {
        // Read data
    }

    @Override
    public void onCancelled(@NonNull DatabaseError error) {
    }
});

Activity

  1. lehcar09 commented on Jul 17, 2026

    @lehcar09
    Contributor

    Hi @chodnagho, thank you for reaching out. I tried reproducing the issue, however, I can't seem to encounter the error you shared.

    Here's the code I used:

    	    val ref = FirebaseDatabase.getInstance()
    		    .getReference("test")
    	    ref.keepSynced(true)
    	    ref.get().addOnCompleteListener(OnCompleteListener { task: Task<DataSnapshot> ->
    				if(task.isSuccessful) {
    					println("Result: ${task.result}")
    				} else {
    					print("Error: ${task.exception}")
    				}
    	    })
    	    
    	    //Result: DataSnapshot { key = test, value = {key=some value} }
    

    And I'm also using BoM 34.16.0. Did I forget something here?

  2. chodnagho commented on Jul 17, 2026

    @chodnagho
    Author

    Hi @lehcar09, thanks for checking.

    Your example works for me too. The issue does not happen on the first call.

    The difference is that in my app keepSynced(true) is called again on the same DatabaseReference after the previous sync state already exists.

    I can reproduce it consistently only when the same reference is reused/recreated and keepSynced(true) is invoked again after the first successful sync.

    Steps to reproduce:

    1. Open the dialog/activity for the first time.

    Code:

    DatabaseReference userRef = Users.child(uid);
    
    userRef.keepSynced(true);
    
    userRef.get()
        .addOnSuccessListener(snapshot -> {
            // update UI
        });

    The first time works correctly.

    1. Close the dialog/activity.

    2. Open the same dialog/activity again.

    The same code runs again:

    userRef.keepSynced(true);
    userRef.get();

    The second time the app crashes.

    Crash:

    java.lang.RuntimeException: Uncaught exception in Firebase Database runloop (22.0.1)
    
    Caused by:
    java.lang.AssertionError:
    hardAssert failed: listen() called twice for same QuerySpec
    

    Relevant stack:

    Repo.keepSynced()
    Query.keepSynced()
    SyncTree.addEventRegistration()
    PersistentConnectionImpl.listen()
    

    Environment:

    • Firebase Realtime Database SDK: 22.0.1
    • Firebase BoM: 34.16.0
    • Language: Java
    • Persistence: Disabled
    • Platform: Android

    The important part seems to be repeated keepSynced(true) calls on the same path during the app lifecycle.

    Using:

    userRef.keepSynced(true);
    
    userRef.addListenerForSingleValueEvent(new ValueEventListener() {
        @Override
        public void onDataChange(@NonNull DataSnapshot snapshot) {
            // works
        }
    
        @Override
        public void onCancelled(@NonNull DatabaseError error) {
        }
    });

    does not reproduce the crash in the same flow.

    I did not enable Firebase persistence. This happens with keepSynced(true) only.

    I hope this additional lifecycle detail helps reproduce the issue.

  3. lehcar09 commented on Jul 20, 2026

    @lehcar09
    Contributor

    Thank you for clarification and additional details @chodnagho. I was able to reproduce the issue. I'll raise this to our engineers and see what we can do here. Thanks!

  4. chodnagho commented on Jul 31, 2026

    @chodnagho
    Author

    Thank you, lehcar09. It's quite ironic that I caught this issue while it went unnoticed for so long in a Google-maintained SDK. For a company with Google's resources, that's genuinely surprising.

  5. mvanhorn commented on Aug 20, 2026

    @mvanhorn

    Implemented in #8544.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

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