Database corruption no longer shows `DIALOG_LOAD_FAILED`: `SqliteFailure`; `DatabaseCorrupt`; `database disk image is malformed`
See screenshot at:
https://www.reddit.com/r/MedSchoolAnkiIndia/comments/1mpr10h/what_does_this_even_mean/
The error must still be displayed as we want it when users send a screenshot. But start with a localized message
Something as vague as "An error occurred. Tap Ok to resume using ankidroid." That would already be a slight improvement.
May be also adding a second button "restore" that would display the same menu as when we can't open the collection, offering to full download, redtore backup, do a check, etx...
@Arthur-Milchior Looks good! Can I have a go at this?
I’d like to work on this. The current error message is confusing, so I’ll replace it with a localized fallback like “An error occurred. Tap OK to continue using AnkiDroid.” while still keeping the full error details in the logs for debugging. I’ll also add a second button, “Restore,” that opens the same recovery menu we already show when the collection can’t be opened. That way users either just continue or immediately get recovery options without having to dig around.
@Amitesh-exp it would be fair to let @thatrajeevkr have a go, as they asked first (sorry! & thanks for the enthusiasm)
I feel that one of our existing dialogs are not shown when it should be: DIALOG_LOAD_FAILED is likely what we're after
https://github.com/ankidroid/Anki-Android/blob/ff14dd0b63cfbfdc0d6099c647dd72d2d45460aa/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DatabaseErrorDialog.kt#L622-L645
https://github.com/ankidroid/Anki-Android/blob/ff14dd0b63cfbfdc0d6099c647dd72d2d45460aa/AnkiDroid/src/main/java/com/ichi2/anki/dialogs/DatabaseErrorDialog.kt#L552-L560
@david-allison , I’d like to take this up if @thatrajeevkr is not working on it. I’m interested in addressing the issue and implementing the localized error message as discussed. If no one else is actively working on it, I can get started.
Give them 48 hours to reply, if you don't hear back, feel free!
@david-allison i am working on a separate thing right now, It would be fine if someone else wants to work on it.
@vikasp07 sounds good to go!
@david-allison I’ll add a short friendly message to DIALOG_LOAD_FAILED (“An error occurred…”) and then show the detailed error below it. I’ll also add a Restore button that opens the repair/restore screen (using the existing DIALOG_REPAIR_COLLECTION), and define the new strings in 03-dialogs.xml.
What's the reason for not showing the dialog unchanged?
@david-allison I’ll replace the visible DIALOG_LOAD_FAILED text with a short localized fallback: “An error occurred. Tap OK to continue using AnkiDroid.” i was just thinking that the full diagnostic will still be written to the app log for debugging, so we don’t lose any troubleshooting info.
I don't understand why the dialog isn't appropriate as-is
The existing dialog shows raw sqlite errors that confuse users.
I don't believe the displayed error is DIALOG_LOAD_FAILED
@david-allison ,I think this error: ErrorDbError { info: "Sqlite Failure (Error { code:DatabaseCorrupt, extended_code: 11}, Some("database disk image is malformed"))", kind: Other } should map to DIALOG_LOAD_FAILED (i.e. DIALOG_LOAD_FAILED -> databaseCorruptFlag). If that’s not correct, which dialog should it map to instead according to you?
After the pull request I investigated further and tried to find (R.string.corrupt_db_message), but I couldn’t find any references to it. Do you know where that string is defined or why the dialog might not be showing? and What should I focus on to solve this issue?
I'm not on my PC, brief responses may not be sufficient/accurate:
A corrupt database error should lead to a corrupt database state. If the error which this produces is insufficient, then we improve the error
I strongly suspect the linked error is coming from launchCatchingTask, which already has code for handing different types of errors (but not this one).
Strings are defined in res/values/*.xml, you should be able to search for them within the project, or click "go to definition"
@david-allison yes launchCatchingTask doesn’t currently have explicit handling for database corruption errors such as ErrorDbError or Android’s SQLiteDatabaseCorruptException. Instead, it just catches a generic Exception and calls showError to display a dialog. So yes, we should extend it to handle corruption cases directly and set the proper dialog state.
Also, I tried to look for R.string.corrupt_db_message in res/values/*.xml, but I couldn’t find any definition for it. That likely means the string resource is missing.
Ok, firstly reproduce the error with a corrupt collection, you should be able to find a reference to one in git log, or in Anki-Android-Backend
You may then want to change the exception which the Anki-Android-Backend repository produces. There's various tests here, but it may not have passed through correctly:
https://github.com/ankidroid/Anki-Android-Backend/tree/main/rsdroid-instrumented/src/androidTest/java/net/ankiweb/rsdroid/database
The string exists, it would be a compiler error otherwise:
- Double tap shift to search
- With the string selected:
- Right click => 'go to declaration/usages'
- Command + click the string
- Ctrl+Shift+F to search the project
https://github.com/ankidroid/Anki-Android/blob/e43c3f75dbc7ab3135277a0c5a07447b8c43f305/AnkiDroid/src/main/res/values/09-backup.xml#L40
@david-allison, Below is a concise problem summary, where I found things, and a proposed fix for this error :
[Since,] database corruption errors (e.g. SQLiteDatabaseCorruptException and our ErrorDbError) are not handled explicitly in the app’s central error path. They fall through to the generic exception handling in CoroutineHelpers.kt (the runCatching helper), which only calls showError for a generic exception. As a result we do not set DatabaseErrorDialog.databaseCorruptFlag = true and the app does not surface the DIALOG_LOAD_FAILED (corrupt DB) dialog with the intended corrupt_db_message to the user.
Location of the issue
File: CoroutineHelpers.kt
Function: runCatching / launchCatchingTask (the generic exception handling path)
- I suspect the error is being raised inside
launchCatchingTask/runCatching, which currently lacks explicit handling for corruption-specific exceptions.
Proposed solution (suggested patch)
Add explicit handling for corruption exceptions in runCatching so we set the corrupt-DB flag and show the proper dialog. Example Kotlin patch:
import android.database.sqlite.SQLiteDatabaseCorruptException
// import the ErrorDbError type if needed
// import net.anki.*.ErrorDbError
// ...existing code...
try {
return block()
} catch (exc: Exception) {
// existing handling for other exceptions...
when (exc) {
// existing branches...
is SQLiteDatabaseCorruptException, is ErrorDbError -> {
// Mark that the database is corrupt so the correct dialog is shown
DatabaseErrorDialog.databaseCorruptFlag = true
// Show the corruption-specific error so launchCatchingTask / showError
// will present the DIALOG_LOAD_FAILED dialog with the corrupt DB message.
// Adjust the showError call to match existing helpers / crash-report API.
showError("Database corruption detected", exc.toCrashReportData(this))
// Optionally explicitly trigger DIALOG_LOAD_FAILED here if needed by the flow:
// showDialog(DIALOG_LOAD_FAILED) // <- only if appropriate in this context
// Return / rethrow as your existing flow expects (null/Unit/throw)
return null
}
else -> {
// existing fallback handling
}
}
}
- Ensures corruption errors set
DatabaseErrorDialog.databaseCorruptFlag = true. - Causes the app to show the correct "corrupt database" dialog (the string in
09-backup.xml), giving users the restore/backup options instead of a generic error. - Keeps other exception handling unchanged.
Should I open a small PR changing only runCatching in CoroutineHelpers.kt (and add a unit/instrumented test to simulate corrupt-DB error) or do you prefer the change be made where launchCatchingTask is called?
That's a really verbose explanation
Have you confirmed that the error is a SQLiteDatabaseCorruptException?
@david-allison I haven’t yet confirmed if the error is a SQLiteDatabaseCorruptException. I’m trying this for the first time, so I don’t have much idea about the best approach. From above, I think I should: Reproduce the issue by opening a deliberately corrupted collection (from git log or Anki-Android-Backend). Run/debug and check the exact exception type (SQLiteDatabaseCorruptException, ErrorDbError, etc.).
Could you guide me on the way to create or obtain a corrupt collection for testing?
Ok, firstly reproduce the error with a corrupt collection, you should be able to find a reference to one in
git log, or inAnki-Android-BackendYou may then want to change the exception which the
Anki-Android-Backendrepository produces. There's various tests here, but it may not have passed through correctly:https://github.com/ankidroid/Anki-Android-Backend/tree/main/rsdroid-instrumented/src/androidTest/java/net/ankiweb/rsdroid/database
@david-allison This is my first time contributing, so I haven’t set up the project locally yet as i was finding good first issue. Could you guide me on how to test changes (e.g., run it in Android Studio or another way)? Also, after reviewing several files and getting interested in the project, would you recommend that I continue with this issue or try another one?
We've been talking for a couple of weeks and you're not at the stage where you've set up the project locally (normally the first step in development). You need to be able to execute code to fully understand the problem.
If you want an issue, besides our onboarding issue, this is the best to get started.
In Android Studio, you can use the emulator to test changes, and the debugger/logcat otherwise.
I could take this on if that's okay with everyone, but if someone else would be better suited then please go ahead.
Sounds good, cheers!
@david-allison Hey again, should've asked for some help sooner, but I've been having config issues with the backend. I have Anki-Android and Anki-Android-Backend as siblings in a directory. I have followed the directions in https://github.com/ankidroid/Anki-Android-Backend/blob/main/README.md, and it seems the .aar and .jar are generated successfully. Despite that, when I set local_backend=true,
I get this message when trying to run ./gradlewjacocoTestReport within Anki-Android (or anything that requires the backend):
FAILURE: Build failed with an exception.
* What went wrong:
Execution failed for task ':libanki:testutils:compileDebugKotlin'.
> Error while evaluating property 'friendPathsSet$kotlin_gradle_plugin_common' of task ':libanki:testutils:compileDebugKotlin'.
> Could not resolve all files for configuration ':libanki:testutils:debugCompileClasspath'.
> Failed to transform rsdroid-release.aar to match attributes {artifactType=android-classes-jar, org.gradle.usage=java-api}.
> Execution failed for AarToClassTransform: /home/alex/projects/Anki-Android/Anki-Android-Backend/rsdroid/build/outputs/aar/rsdroid-release.aar.
> /home/alex/projects/Anki-Android/Anki-Android-Backend/rsdroid/build/outputs/aar/rsdroid-release.aar
> Failed to transform rsdroid-testing.jar to match attributes {artifactType=android-classes-jar, org.gradle.libraryelements=jar, org.gradle.usage=java-runtime}.
> Execution failed for IdentityTransform: /home/alex/projects/Anki-Android/Anki-Android-Backend/rsdroid-testing/build/libs/rsdroid-testing.jar.
> File/directory does not exist: /home/alex/projects/Anki-Android/Anki-Android-Backend/rsdroid-testing/build/libs/rsdroid-testing.jar
The file paths seem strange. I thought it might be something corrupt in my gradle configurations but resetting it hasn't helped. I'm using Linux-AMD64. Any idea?
Could you come to Discord: invite => #ankidroid-dev
I'm probably not going to have the time to dig in deep here
/home/alex/projects/Anki-Android/Anki-Android-Backend/rsdroid-testing/build/libs/rsdroid-testing.jar is definitely the issue
- /home/alex/projects/Anki-Android/Anki-Android-Backend/rsdroid-testing/build/libs/rsdroid-testing.jar
+ /home/alex/projects/Anki-Android-Backend/rsdroid-testing/build/libs/rsdroid-testing.jar
This is likely my mistake, profuse apologies. The line to fix is:
https://github.com/ankidroid/Anki-Android/blob/91edb89744cf9b0b116aba5775e0643e5d0e0421/libanki/build.gradle.kts#L52-L53
- #18788
- https://github.com/ankidroid/Anki-Android/commit/4b64e8f51f5db605f1466255b06e528a22b76fac#diff-23a292ffd0f84430aec43ee601434c9b976246fd31742688b48ef7050017b6a4R48
I get this message when trying to run
./gradlewjacocoTestReportwithinAnki-Android(or anything that requires the backend):
Got it working now.
Really sorry about the long wait for this one, life happened. I am back now and I figured some things out.