Bug Description
After a hot restart on Flutter web (DDC debug mode), SqliteAsyncDriftConnection's BroadcastChannel listener crashes with a TypeError. This kills all Drift watch query update notifications, resulting in stale data. The error does not occur on:
- Fresh app launch
- Full page reload (Ctrl+Shift+R)
- Hot reload
Error
DartError: TypeError: Instance of 'LegacyJavaScriptObject': type 'LegacyJavaScriptObject' is not a subtype of type 'UpdateNotification?'
dart-sdk/lib/_internal/js_dev_runtime/private/ddc_runtime/errors.dart 274:3 throw_
dart-sdk/lib/_internal/js_shared/lib/rti.dart 1516:3 _generalNullableAsCheckImplementation
dart-sdk/lib/_internal/js_shared/lib/rti.dart 89:34 _installSpecializedAsCheck
dart-sdk/lib/async/stream_pipe.dart 152:15 [_add]
dart-sdk/lib/async/stream_pipe.dart 252:9 [_handleData]
dart-sdk/lib/async/stream_pipe.dart 184:5 [_handleData]
package:web/src/helpers/events/streams.dart 165:63 <fn>
dart-sdk/lib/_internal/js_dev_runtime/patch/js_allow_interop_patch.dart 224:27 _callDartFunctionFast1
Root Cause
Hot restart recompiles Dart code and regenerates DDC runtime type information. However, the JavaScript BroadcastChannel.onmessage callback (created by SqliteAsyncDriftConnection via sqlite_async's BroadcastUpdates) retains references to the old DDC type system. When BroadcastChannel fires after hot restart, the old callback casts the message using stale type references → TypeError → listener dies → all future Drift watch query updates lost.
The crash occurs in sqlite_async/lib/src/web/database/broadcast_updates.dart where event.data is cast to _BroadcastMessage. After hot restart, the DDC runtime cannot resolve the type, and it arrives as a raw LegacyJavaScriptObject.
Impact
- Dev workflow: Hot restart is unusable on web — developers must use full page refresh
- Production: Not affected (release builds don't hot restart)
Environment
powersync: 1.18.0
powersync_core: 1.8.0
drift_sqlite_async: 0.2.6
sqlite_async: 0.13.1
drift: 2.31.0
- Dart SDK: 3.9.0
- Flutter web, DDC (debug mode)
- Chrome 130+
- macOS
Steps to Reproduce
- Create a Flutter web app with PowerSync + Drift using
SqliteAsyncDriftConnection
- Run on Chrome in debug mode (DDC):
flutter run -d chrome
- Verify data loads and watch queries work (Drift streams emit)
- Trigger a hot restart (Shift+R in terminal, or restart from IDE)
- Perform any database write that triggers a BroadcastChannel notification
- Observe the
LegacyJavaScriptObject error in console
- Drift watch queries stop updating — data is stale
Workaround
Use full page refresh (Ctrl+Shift+R) instead of hot restart during web development.
Suggested Fix
The BroadcastUpdates class in sqlite_async could wrap the BroadcastChannel.onmessage handler with error handling, or re-register the listener on hot restart. Alternatively, the _BroadcastMessage JS interop type could use a more resilient deserialization path that doesn't rely on DDC runtime type identity.
Bug Description
After a hot restart on Flutter web (DDC debug mode),
SqliteAsyncDriftConnection's BroadcastChannel listener crashes with aTypeError. This kills all Drift watch query update notifications, resulting in stale data. The error does not occur on:Error
Root Cause
Hot restart recompiles Dart code and regenerates DDC runtime type information. However, the JavaScript
BroadcastChannel.onmessagecallback (created bySqliteAsyncDriftConnectionviasqlite_async'sBroadcastUpdates) retains references to the old DDC type system. When BroadcastChannel fires after hot restart, the old callback casts the message using stale type references →TypeError→ listener dies → all future Drift watch query updates lost.The crash occurs in
sqlite_async/lib/src/web/database/broadcast_updates.dartwhereevent.datais cast to_BroadcastMessage. After hot restart, the DDC runtime cannot resolve the type, and it arrives as a rawLegacyJavaScriptObject.Impact
Environment
powersync: 1.18.0powersync_core: 1.8.0drift_sqlite_async: 0.2.6sqlite_async: 0.13.1drift: 2.31.0Steps to Reproduce
SqliteAsyncDriftConnectionflutter run -d chromeLegacyJavaScriptObjecterror in consoleWorkaround
Use full page refresh (Ctrl+Shift+R) instead of hot restart during web development.
Suggested Fix
The
BroadcastUpdatesclass insqlite_asynccould wrap theBroadcastChannel.onmessagehandler with error handling, or re-register the listener on hot restart. Alternatively, the_BroadcastMessageJS interop type could use a more resilient deserialization path that doesn't rely on DDC runtime type identity.