A WorkManager background task can hold the database open when the foreground app starts. PRAGMA journal_mode = WAL on the second connection then fails with SQLITE_BUSY_SNAPSHOT (extended code 261, primary code 5), crashing before the UI renders.
Move PRAGMA busy_timeout = 5000 before the WAL pragma so SQLite auto-retries plain SQLITE_BUSY for up to 5 s.
Extract the setup logic into a named _setupPragmas function and catch SqliteException with resultCode == 5 (covers both SQLITE_BUSY and SQLITE_BUSY_SNAPSHOT). SQLITE_BUSY_SNAPSHOT only occurs when the DB is already in WAL mode, so the pragma is effectively a no-op and it is safe to continue.
New regression test in test/unit/migration_test.dart (WAL setup (#508)) opens a second connection while a read transaction holds a WAL snapshot and verifies setupPragmasForTesting does not throw.
All existing migration tests still pass.
flutter test test/unit/background_sync_test.dart test/unit/database_path_test.dart passes.
## Summary
- A WorkManager background task can hold the database open when the foreground app starts. `PRAGMA journal_mode = WAL` on the second connection then fails with `SQLITE_BUSY_SNAPSHOT` (extended code 261, primary code 5), crashing before the UI renders.
- Move `PRAGMA busy_timeout = 5000` before the WAL pragma so SQLite auto-retries plain `SQLITE_BUSY` for up to 5 s.
- Extract the setup logic into a named `_setupPragmas` function and catch `SqliteException` with `resultCode == 5` (covers both `SQLITE_BUSY` and `SQLITE_BUSY_SNAPSHOT`). `SQLITE_BUSY_SNAPSHOT` only occurs when the DB is already in WAL mode, so the pragma is effectively a no-op and it is safe to continue.
Closes #508
## Test plan
- [x] New regression test in `test/unit/migration_test.dart` (`WAL setup (#508)`) opens a second connection while a read transaction holds a WAL snapshot and verifies `setupPragmasForTesting` does not throw.
- [x] All existing migration tests still pass.
- [x] `flutter test test/unit/background_sync_test.dart test/unit/database_path_test.dart` passes.
🤖 Generated with [Claude Code](https://claude.com/claude-code)
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
Summary
PRAGMA journal_mode = WALon the second connection then fails withSQLITE_BUSY_SNAPSHOT(extended code 261, primary code 5), crashing before the UI renders.PRAGMA busy_timeout = 5000before the WAL pragma so SQLite auto-retries plainSQLITE_BUSYfor up to 5 s._setupPragmasfunction and catchSqliteExceptionwithresultCode == 5(covers bothSQLITE_BUSYandSQLITE_BUSY_SNAPSHOT).SQLITE_BUSY_SNAPSHOTonly occurs when the DB is already in WAL mode, so the pragma is effectively a no-op and it is safe to continue.Closes #508
Test plan
test/unit/migration_test.dart(WAL setup (#508)) opens a second connection while a read transaction holds a WAL snapshot and verifiessetupPragmasForTestingdoes not throw.flutter test test/unit/background_sync_test.dart test/unit/database_path_test.dartpasses.🤖 Generated with Claude Code