Summary
We see recurring SqliteException(21): powersync_control: invalid state: No iteration is active
on Android, on SDK versions that already contain the control-flow refactor from #410
(we have run 2.1.0, 2.3.0 and 2.3.1).
What we observe
- Android only. Our web build never produced this error.
- Across 13 devices, independent of app build and independent of upload health.
- Correlates with two conditions: access token expiry (we log
JWT has expired followed by a burst
of these errors within ~2s), and network churn (one device logged 3522 network failures over 7 days).
- Usually transient and self-healing within seconds. We have also recorded sustained episodes —
longest 115 minutes. During those, downloads stop while local writes and uploads keep working,
so the device stops seeing other devices' changes.
disconnect() + connect() does not recover it. One device logged 92 reconnects over 2 hours
with lastSyncedAt frozen throughout. A full process restart does recover it.
What we found in the source
In powersync-sqlite-core (crates/core/src/sync/streaming_sync.rs) the core moves itself to
ClientState::Idle both on any error returned from handle.run() and on normal completion —
run() returns done = true in the same invocation in which it pushes Instruction::CloseSyncStream.
Any subsequent SyncEvent then produces this error. Same in core v0.4.13 and v0.5.x.
In the Dart client, two senders push into sync._nonLineSyncEvents — a broadcast controller owned by
StreamingSyncImplementation, so it outlives individual iterations — guarded only by !sync.aborted:
packages/powersync/lib/src/sync/streaming_sync.dart (2.3.1, ~L264): _uploadAllCrud().whenComplete
adds UploadCompleted. The CRUD loop runs concurrently with iterations and is driven by local writes.
- same file, ~L581:
FetchCredentials(didExpire: false) starts a fire-and-forget
prefetchCredentials().then((_) { if (!sync.aborted) _nonLineSyncEvents.add(TokenRefreshComplete()); }),
whose resolution time depends on the network.
Environment
powersync 2.1.0 / 2.3.0 / 2.3.1 (Flutter), core extension v0.4.13
- Flutter 3.41.9 (Dart 3.11.5), Android, Samsung SM-T500 tablets and similar
- Backend: Supabase Postgres + PowerSync Cloud
Summary
We see recurring
SqliteException(21): powersync_control: invalid state: No iteration is activeon Android, on SDK versions that already contain the control-flow refactor from #410
(we have run 2.1.0, 2.3.0 and 2.3.1).
What we observe
JWT has expiredfollowed by a burstof these errors within ~2s), and network churn (one device logged 3522 network failures over 7 days).
longest 115 minutes. During those, downloads stop while local writes and uploads keep working,
so the device stops seeing other devices' changes.
disconnect()+connect()does not recover it. One device logged 92 reconnects over 2 hourswith
lastSyncedAtfrozen throughout. A full process restart does recover it.What we found in the source
In
powersync-sqlite-core(crates/core/src/sync/streaming_sync.rs) the core moves itself toClientState::Idleboth on any error returned fromhandle.run()and on normal completion —run()returnsdone = truein the same invocation in which it pushesInstruction::CloseSyncStream.Any subsequent
SyncEventthen produces this error. Same in core v0.4.13 and v0.5.x.In the Dart client, two senders push into
sync._nonLineSyncEvents— a broadcast controller owned byStreamingSyncImplementation, so it outlives individual iterations — guarded only by!sync.aborted:packages/powersync/lib/src/sync/streaming_sync.dart(2.3.1, ~L264):_uploadAllCrud().whenCompleteadds
UploadCompleted. The CRUD loop runs concurrently with iterations and is driven by local writes.FetchCredentials(didExpire: false)starts a fire-and-forgetprefetchCredentials().then((_) { if (!sync.aborted) _nonLineSyncEvents.add(TokenRefreshComplete()); }),whose resolution time depends on the network.
Environment
powersync2.1.0 / 2.3.0 / 2.3.1 (Flutter), core extension v0.4.13