Skip to content
Back

πŸ› Realtime Flutter SDK Crash – RealtimeMessage.fromMap TypeError

  • 0
  • Flutter
  • Realtime
  • Cloud
!
1 Mar, 2026, 12:33

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

TypeScript
#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>

TypeScript
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)

TypeScript
it throws:

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

TypeScript
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); });

TypeScript
As soon as a document `.create` or `.update` event is triggered,
the SDK crashes in `RealtimeMessage.fromMap`.
TL;DR
Developers encountered crashes in the Realtime Flutter SDK due to a TypeError in RealtimeMessage.fromMap. The proposed fix involves making the parsing function tolerant to both List and Map inputs. By applying the provided patch, Realtime functions reliably without crashes, solving the issue before user code handles errors. This crash renders Realtime unusable in production Flutter apps but can be quickly fixed with the small, backward-compatible change proposed.
!
1 Mar, 2026, 12:33

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

Example patch:

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

Reply to this thread by joining our Discord

Reply on Discord

Need support?

Join our Discord

Get community support by joining our Discord server.

Join Discord

Get premium support

Join Appwrite Pro and get email support from our team.

Learn more