Skip to content

Backup ZIP can contain empty NotallyDatabase because export only copies main DB file after an unverified WAL checkpoint #1062

Description

@VVincentt

What happened?

Hi,
I am migrating from Omni Notes because it is dated and reminders do not work well anymore. I found this project and it is exactly what I was looking for. Thanks a lot for this application. I was able to to migrate my notes to NotallyX and I tested the backup/export feature and I found that it was not working. The export is empty. I have asked AI to help me determine if I was doing something wrong or if there is a bug in the code. Here is the analysis:

Steps that reproduced the issue in my case

  1. Install NotallyX 7.11.2 on Android 16.
  2. Import a database containing notes (89 notes, 6 labels) with the default internal storage.
  3. Go to Settings → Data → Store data in public folder and enable it.
  4. Create or edit a note.
  5. Export a backup via Settings → Backups → Export backup.
  6. Extract NotallyDatabase from the generated .zip.
  7. Open the extracted database with any SQLite viewer.

Expected behavior

The extracted NotallyDatabase should contain all notes and labels (e.g. 89 BaseNote rows, 6 Label rows).

Actual behavior

  • The app toast reports the correct number of backed-up notes (e.g. 90).
  • The extracted NotallyDatabase has 0 BaseNote rows and 0 Label rows.
  • The database file is only ~36 KB (schema only) instead of ~110–124 KB.
  • The public-folder copy of the database is also empty (36 KB, 0 rows).
  • Auto backups produced the same empty database.

Workaround that worked

Clearing app data, reinstalling, importing the same database with "Store data in public folder" disabled, force-closing the app, and exporting again produced a valid 110 KB backup with 89 notes and 6 labels.

Suspected root cause

In ExportExtensions.kt, copyDatabase() calls database.checkpoint() and then copies only the main database file (no -wal / -shm files). The checkpoint implementation in NotallyDatabase.kt is:

fun checkpoint() {
    getBaseNoteDao().query(SimpleSQLiteQuery("pragma wal_checkpoint(FULL)"))
}

The @RawQuery DAO method returns Int and the return value is ignored, so a failed or busy checkpoint is not detected. If WAL frames are not flushed to the main file, the app still displays notes (SQLite reads the WAL), but the copied main file is empty and ends up in the backup.

Enabling "Store data in public folder" appears to trigger the bug in this case, because enableDataInPublic() uses the same fragile checkpoint to copy the database to the public folder. However, the underlying checkpoint/copy bug could theoretically also affect the default internal storage if the WAL checkpoint fails for any reason (active connections, busy state, etc.).

Suggested fix

  • Verify the result of PRAGMA wal_checkpoint(FULL) and retry on busy.
  • Close all database connections before copying the database file.
  • Alternatively, include the -wal and -shm files in the backup ZIP when the database is in WAL mode.
  • Apply the same robust handling to enableDataInPublic() / disableDataInPublic().

Related issue

Possibly related to #1061 (SQLITE_CORRUPT / all notes gone on Android 16 / One UI 8.5 / 7.11.2).

Note for maintainers

I am not certain whether "Store data in public folder" is strictly required to reproduce this, or whether the default internal storage can also be affected after WAL frames accumulate. The common factor is the unverified checkpoint before copying only the main database file.

App Version

7.11.2 F-droid

Android Version (API Level)

16 (API 36)

(Optional) Relevant log output

2026-06-17 16:07:38 - BaseNoteModel - Exporting backup to 'content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fmedia%2Fcom.philkes.notallyx%2FNotallyX%20Backup%202026-06-17%2016-07.zip'...
2026-06-17 16:07:38 - BaseNoteModel - Finished exporting backup to 'content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fmedia%2Fcom.philkes.notallyx%2FNotallyX%20Backup%202026-06-17%2016-07.zip'
2026-06-17 16:14:25 - BaseNoteModel - Exporting backup to 'content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fmedia%2Fcom.philkes.notallyx%2FNotallyX%20Backup%202026-06-17%2016-14.zip'...
2026-06-17 16:14:25 - BaseNoteModel - Finished exporting backup to 'content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fmedia%2Fcom.philkes.notallyx%2FNotallyX%20Backup%202026-06-17%2016-14.zip'
2026-06-17 20:53:44 - BaseNoteModel - Exporting backup to 'content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fmedia%2Fcom.philkes.notallyx%2FNotallyX%20Backup%202026-06-17%2020-53.zip'...
2026-06-17 20:53:44 - BaseNoteModel - Finished exporting backup to 'content://com.android.externalstorage.documents/document/primary%3AAndroid%2Fmedia%2Fcom.philkes.notallyx%2FNotallyX%20Backup%202026-06-17%2020-53.zip'
2026-06-17 20:54:07 - BaseNoteModel - Exporting backup to 'content://com.android.externalstorage.documents/document/primary%3Abackups%2FNotallyX%20Backup%202026-06-17%2020-54.zip'...
2026-06-17 20:54:08 - BaseNoteModel - Finished exporting backup to 'content://com.android.externalstorage.documents/document/primary%3Abackups%2FNotallyX%20Backup%202026-06-17%2020-54.zip'
2026-06-17 21:24:38 - BaseNoteModel - Exporting backup to 'content://com.android.externalstorage.documents/document/primary%3Abackups%2FNotallyX%20Backup%202026-06-17%2021-24.zip'...
2026-06-17 21:24:38 - BaseNoteModel - Finished exporting backup to 'content://com.android.externalstorage.documents/document/primary%3Abackups%2FNotallyX%20Backup%202026-06-17%2021-24.zip'

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions