#🐛 Realtime Flutter SDK Crash – RealtimeMessage.fromMap TypeError

14 messages · Page 1 of 1 (latest)

atomic crag
#

Summary
When using Appwrite Cloud with the Flutter SDK (latest appwrite release: 21.4.0), Realtime crashes with:

#0      new RealtimeMessage.fromMap (package:appwrite/src/realtime_message.dart:57:47)```
The crash occurs as soon as a real Realtime event (`.create`, `.update`, `.delete`) is received. Heartbeat messages work fine.

This happens before the event reaches the user’s sub.stream.listen(...) callback.


**Environment**
- Appwrite Cloud
- Endpoint: https://cloud.appwrite.io/v1
- Flutter SDK: appwrite: 21.4.0
- Platform: Android (Flutter)


**Root Cause**
Inside the Flutter SDK, RealtimeMessage.fromMap assumes that:

events: List<String>
channels: List<String>

However, under certain conditions, the Realtime WebSocket payload delivers `events` and/or `channels` as a Map instead of a `List`.

Because the SDK strictly casts:

(map['events'] as List)

it throws:

type '_Map<String, dynamic>' is not a subtype of type 'Iterable<dynamic>'

This indicates the parser is not tolerant to slight payload structure variations.


**Reproduction**
Minimal example:

final sub = realtime.subscribe([
'databases.{dbId}.collections.{collectionId}.documents'
]);

sub.stream.listen((event) {
print(event);
});

As soon as a document `.create` or `.update` event is triggered,
the SDK crashes in `RealtimeMessage.fromMap`.
#

Proposed Fix (Robust Parsing)
Making RealtimeMessage.fromMap tolerant to both List and Map resolves the issue completely.

Example patch:

static List<String> _coerceStringList(dynamic v) {
  if (v == null) return const [];
  if (v is List) return v.map((e) => e.toString()).toList();
  if (v is Map) return v.values.map((e) => e.toString()).toList();
  return [v.toString()];
}

factory RealtimeMessage.fromMap(Map<String, dynamic> map) {
  final dynamic data = map['data'];
  final Map<String, dynamic> m =
      data is Map<String, dynamic> ? data : map;

  return RealtimeMessage(
    events: _coerceStringList(m['events']),
    payload: Map<String, dynamic>.from(m['payload'] ?? const {}),
    channels: _coerceStringList(m['channels']),
    timestamp: (m['timestamp'] ?? '').toString(),
  );
}

After applying this change locally, Realtime works reliably without crashes.

Why This Matters

  • The crash occurs before user code can handle errors.
  • It makes Realtime unusable in production Flutter apps.
  • The fix is small and backward-compatible.
  • Making parsing tolerant prevents similar crashes in the future if payload shape slightly changes.
vagrant galleon
atomic crag
# vagrant galleon Can you clarify which condition results in a map for the events and channels?

The crash occurs when receiving a realtime message after subscribing to document events.

We subscribe like this:

client.subscribe([
  'databases.$databaseId.collections.$collectionId.documents'
]);```
Under certain conditions, the app crashes inside:
```RealtimeMessage.fromMap```
with a TypeError.

From debugging the payload, it appears that in the failing case the events (and possibly channels) field is returned as a `Map<String, dynamic>` instead of a `List<String>`.

Expected structure:
```json
{
  "events": [
    "databases.*.collections.*.documents.*.create"
  ],
  "channels": [
    "databases.*.collections.*.documents"
  ]
}

Failing structure (example):

{
  "events": {
    "0": "databases.*.collections.*.documents.*.create"
  }
}

This causes RealtimeMessage.fromMap to throw because it expects a List<String>.
It does not happen consistently — only in certain document update/create events.

Could you clarify under which conditions the backend would serialize events or channels as a map instead of a list?

Thanks!

vagrant galleon
#

I see it's been fixed here.

#

and I believe this has been released on Cloud

atomic crag
#

I can't find its code so I can't be 100% sure on that, but I'll be looking forward to use the release code again ^^

atomic crag
vagrant galleon
atomic crag
vagrant galleon
#

if there are still any issues, it would really help if we can get some way to reproduce

atomic crag