Summary
We hit the exact failure class that #147 fixes, in production, on a released SDK version (1.14.3) — and since the latest release (1.14.4, June 22) predates that merge (June 29), every app on a released version is still exposed. Requesting a release that includes #147.
What we observed (production, iOS 26.5.2, SDK 1.14.3)
One second after a cold app launch:
sqliteError(extendedResultCode: 261, offset: nil,
message: Optional("database is locked"),
errorString: Optional("database is locked"),
sql: Optional("pragma journal_mode = WAL"))
Extended code 261 = SQLITE_BUSY_RECOVERY. Root cause on 1.14.3: configureConnection runs pragma journal_mode = WAL before its own pragma busy_timeout = 30000, so the WAL transition executes at the default busy_timeout = 0 and throws instantly on a transient recovery/contention lock instead of waiting. The connect then fails and every watch stream consuming the database dies.
We reproduced it deterministically in a test: open a file-backed PowerSyncDatabase while a competing connection holds an EXCLUSIVE lock → the open throws the BUSY family at pragma journal_mode = WAL. Arming busy_timeout first (via the public initialStatements: parameter — our current workaround) makes the same contended open wait out the lock and succeed.
#147 fixes this properly on main (busy_timeout ordered first + the async open-retry loop covering the handler-less WAL-transition case). It just hasn't shipped.
Ask
A tagged release including #147. Happy to provide any more detail from our production event or the reproduction test.
Summary
We hit the exact failure class that #147 fixes, in production, on a released SDK version (1.14.3) — and since the latest release (1.14.4, June 22) predates that merge (June 29), every app on a released version is still exposed. Requesting a release that includes #147.
What we observed (production, iOS 26.5.2, SDK 1.14.3)
One second after a cold app launch:
Extended code 261 =
SQLITE_BUSY_RECOVERY. Root cause on 1.14.3:configureConnectionrunspragma journal_mode = WALbefore its ownpragma busy_timeout = 30000, so the WAL transition executes at the defaultbusy_timeout = 0and throws instantly on a transient recovery/contention lock instead of waiting. The connect then fails and everywatchstream consuming the database dies.We reproduced it deterministically in a test: open a file-backed
PowerSyncDatabasewhile a competing connection holds an EXCLUSIVE lock → the open throws the BUSY family atpragma journal_mode = WAL. Armingbusy_timeoutfirst (via the publicinitialStatements:parameter — our current workaround) makes the same contended open wait out the lock and succeed.#147 fixes this properly on
main(busy_timeout ordered first + the async open-retry loop covering the handler-less WAL-transition case). It just hasn't shipped.Ask
A tagged release including #147. Happy to provide any more detail from our production event or the reproduction test.