fix: prevent permanent Android startup crash from ObjectBox corruption and malformed server URL#3048
Open
richardtru wants to merge 3 commits into
Conversation
- add one-shot corruption recovery in _initDatabaseMobile and _initDatabaseDesktop: back up the corrupted objectbox dir and retry with a fresh store, preventing the "permanent blank screen after days" pattern caused by oom kills or power-off mid-write-transaction - move initStartupServices inside the try/catch in main.dart so any startup failure (including recovery exhaustion) shows the FailureToStart widget instead of a silent blank screen - validate server url with java.net.URI before passing to socket.io to prevent the IllegalArgumentException crash on malformed percent-encoding (github issue BlueBubblesApp#2845) - extract _openOrAttachStore, _recoverCorruptedDatabase, _pruneCorruptedBackups as dedicated helpers; add _maxBackups constant; use async list() instead of listSync() - remove server url from logcat lines to prevent credential leakage
- log only custom header keys (not values) to prevent auth token leakage - fix desktop linux: move exit(0) inside concurrent-access branch so corrupted databases on linux actually get recovery rather than silent instance-exit - add Database.wasRecovered flag + onStartup emission of database-recovered event so ui can show a sync notice when the database was wiped by recovery - sort corrupted backup dirs by extracted timestamp int instead of lexicographic string comparison to correctly order backups when clock could drift
makes the validation path visible in logcat during manual testing
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
This fixes a recurring crash pattern that has affected multiple users over several years: BlueBubbles works correctly for a few days then permanently fails to start (every launch crashes until app data is cleared). Investigation of GitHub issues (#2845 and related) revealed two independent crash paths:
Root Cause 1 — ObjectBox database corruption (primary)
After a phone OOM kill or power-off during a write transaction, the ObjectBox store files in
<app_docs>/objectbox/become corrupted. On the next launch,openStore()throws an exception. Previously there was no recovery path — thelate final Store storefield was never assigned, causingLateInitializationErroron every subsequent access, permanently bricking the app.Fix: Added one-shot corruption recovery to both
_initDatabaseMobileand_initDatabaseDesktop: on any store open failure that isn't a concurrent-access race, the corrupted directory is renamed to a timestamped backup (objectbox_corrupted_<ms>) and a fresh store is created. Up to 2 backups are retained for diagnostics; older ones are pruned automatically.Also moved
StartupTasks.initStartupServices()inside the try/catch inmain.dartso that if recovery exhausts all options, the user sees theFailureToStartwidget instead of a silent blank screen.After recovery,
Database.wasRecoveredis set totrueandonStartup()emits adatabase-recoveredevent so the UI can surface a "Syncing messages…" notice explaining the empty chat list.Root Cause 2 — Malformed server URL crashes the foreground service (issue #2845)
If the stored server URL contains bare
%characters (e.g.%s%), socket.io's JavaURLDecoderthrowsIllegalArgumentExceptionin a background thread outside any try/catch in the foreground service, crashing the process. Since the foreground service starts on every launch whenkeepAppAliveis enabled, the app permanently crashes on every startup.Fix: Validate the URL with
java.net.URI()(synchronous, no I/O) before handing it toIO.socket(). AURISyntaxExceptionupdates the notification to "missing server URL" and returns gracefully.Security improvements (found during review)
Test plan
adb shell am force-stop com.bluebubbles.messaging, then truncate<app_docs>/objectbox/data.mdb, relaunch → app recovers with empty chat list and begins syncing;objectbox_corrupted_*backup dir is presentflutter.serverAddresstohttp://192.168.1.1:1234/%s%testin SharedPreferences, launch → foreground service logs "Server URL stored in preferences is malformed", notification updates, no crashFailureToStartwidget shown (not silent blank screen)objectbox_corrupted_*directories exist