firebase / firebase/flutterfire

[firebase_database, windows] Listener callbacks are delivered on a non-platform thread and crash the process

Open
#18,630 2 comments 0 reactions 0 assignees View on GitHub
blocked: flutter platform: windows plugin: database reproduced type: bug
Dominant language
Dart
Stars
9.3k
Forks
4.1k
Avg merge
1d 18h
Merged PRs (30d)
52

Description

### Bug report

**Which plugins are affected?**
Realtime Database

**Which platforms are affected?**
Windows

**Description**

On Windows, `firebase_database` delivers Realtime Database listener callbacks straight from the
Firebase C++ SDK's callback thread into the Flutter `EventSink`. The engine reports the violation on
every event, and the process eventually dies.

```
[ERROR:flutter/shell/common/shell.cc(1183)] The 'plugins.flutter.io/firebase_database/query/322_...'
channel sent a message from native to Flutter on a non-platform thread. Platform channel messages
must be sent on the platform thread. Failure to do so may result in data loss or crashes, and must
be fixed in the plugin or application code creating that channel.
```

In `windows/firebase_database_plugin.cpp` (12.5.0), the value listener and the child listener both
call `events_->Success(...)` from inside the SDK callback, and there is no `PostTask` / task-runner
hop anywhere in the file:

```cpp
class VL : public firebase::database::ValueListener {
void OnValueChanged(const DataSnapshot& snapshot) override {
...
events_->Success(EncodableValue(event)); // SDK callback thread
}
flutter::EventSink* events_;
};
```

Separately, `OnCancelInternal` runs on the platform thread and deletes the listener:

```cpp
if (value_listener_) {
query_.RemoveValueListener(value_listener_);
delete value_listener_; // races with an in-flight OnValueChanged
value_listener_ = nullptr;
}
```

so cancelling a subscription while a listen response is still being delivered frees the listener out
from under the callback thread.

**Steps to reproduce**

1. Windows desktop app with `firebase_database`.
2. Subscribe and unsubscribe repeatedly so that some cancels land while a listen response is in
flight (this is what screen transitions do in a long-lived session):

```dart
var i = 0;
Timer.periodic(const Duration(milliseconds: 40), (_) {
i++;
// vary the path so each listen creates a new query rather than joining an existing one
final sub = FirebaseDatabase.instance.ref('some/path/${i % 64}').onValue.listen((_) {});
Future.delayed(Duration(milliseconds: i * 7 % 100), sub.cancel);
});
```

3. The console fills with the `non-platform thread` error above, and the process dies (`flutter run`
prints `Lost connection to device.`).

**Expected behavior**

Listener events are marshalled to the platform thread before touching the `EventSink`, and
cancelling a subscription does not free a listener that the SDK may still be calling into.

**Actual behavior**

Every event triggers the engine's non-platform-thread error, and the process terminates. Reproduced
in about 30 seconds, repeatably, on two builds.

### Additional context

We hit this in production, in an app where several clients have to keep the same state in sync over
Realtime Database, so a listener stays attached for the whole session. After switching our Windows
Realtime Database usage from a third-party Dart implementation to `firebase_database`, crashes on
Windows increased. The same app versions on iOS showed no change.

Note this is **not** the same as the C++ SDK's own use-after-free
(firebase/firebase-cpp-sdk#1881, fixed in v13.11.0). We tested both:

| firebase_core / bundled firebase-cpp-sdk | firebase_database | Result |
| --- | --- | --- |
| 4.10.0 / 13.5.0 | 12.4.2 | crashes in ~30s |
| 4.14.0 / **13.11.0** | 12.5.0 | crashes in ~30s |

Reverting our Windows Realtime Database usage to the third-party Dart implementation survived the
same loop for over 10 minutes with no thread-violation errors, so the remaining defect is in this
plugin's Windows implementation rather than in the C++ SDK.

Related, previously closed for lack of detail: #17800 (firebase_auth), #17801 (cloud_firestore).
Those report the same non-platform-thread violation on Windows in sibling plugins, so the fix may
need to be applied consistently.

**Flutter doctor / versions**

- `firebase_core` 4.14.0, `firebase_database` 12.5.0
- Windows Server 2025 x64

Contributor guide

Open the contributing guide

Research direction

Start in windows/firebase_database_plugin.cpp, focusing on VL, the child listener, and OnCancelInternal. Run the repeated subscribe/unsubscribe reproduction on Windows and trace callback and listener lifetimes. Done means listener events reach EventSink on the platform thread and cancellation cannot free a listener while the SDK may still invoke it.

Written by the indexing model from the issue text.

Assessment

Tech stack
cpp, dart, firebase
Domain
databases, desktop-dev
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Clearly specified
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.