firebase / firebase/firebase-android-sdk
Realtime Database Android: keepSynced(true) followed by get() causes AssertionError "listen() called twice for same QuerySpec"
- Dominant language
- Java
- Stars
- 2.6k
- Forks
- 710
- Avg merge
- 2d 23h
- Merged PRs (30d)
- 34
Description
### [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:
```java
DatabaseReference ref = FirebaseDatabase.getInstance()
.getReference("test/path");
ref.keepSynced(true);
ref.get().addOnCompleteListener(task -> {
// Read data
});
```
Working alternative:
```java
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) {
}
});
```
Contributor guide
Research direction
Start by tracing the reported path through SyncTree, Repo.keepSynced, and PersistentConnectionImpl, using the provided Java reproduction as the first check. Compare keepSynced(true) followed by get() with addListenerForSingleValueEvent(). Done means the same query no longer triggers the assertion and get() returns the current data; the payload does not name a test file.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- android, firebase, java
- Domain
- databases, mobile
- Issue type
- Bug
- Difficulty
- 4/5
- Estimated time
- 3-5 days
- Activity status
- Active
- Clarity
- Mostly clear
- Newbie friendliness
- 55/100