Skip to content

Restoring a Secure Backup created on iOS fails on Android with foreign key violations on message.latest_revision_id #14884

Description

@june-fish

Guidelines

  • I have searched open and closed issues for duplicates
  • I am submitting a bug report for existing functionality that does not work as intended
  • This isn't a feature request or a discussion topic

Bug description

I've been trying to move my Signal account from an iPhone 11 to a Pixel 9 using the Signal Secure Backups paid tier. Restoring the exact same backup between iPhones works fine now, but a few months ago did not. Restoring it on Android fails every time I've tried, on both GrapheneOS and stock Pixel OS (and once in Waydroid for fun), with the current stable release (the APK from signal.org, not Google Play) and the newest pre-release from GitHub.

The restore downloads the archive, restores ~800k messages, and then fails during the "finishing" step. On the stable release this failure is completely silent -- the dialog just closes and it goes back to the "restore from a cloud backup" screen with no error. The logcat shows that the import completes, then a final integrity check throws an uncaught IllegalStateException for foreign key violations on message.latest_revision_id, and the process gets killed.

There are only a few violations out of ~800k messages. I am unsure what causes them -- Claude told me it was possibly 3 edited messages whose revision references the iOS exporter writes in a way the Android importer can't resolve -- but I haven't looked into it enough to know.

Recreating the backup from scratch on the iPhone (turn off and delete backup, re-enable, fresh backup) does not help -- the new archive fails identically, so this reproduces from the iOS exporter side, not from one stale archive.

Steps to reproduce

  1. On iOS (latest App Store version), enable Signal Secure Backups (paid tier) and let a backup complete
  2. On a Pixel 9 (GrapheneOS or stock, doesn't matter), install Signal, fresh install
  3. During registration, choose Transfer or restore account -> From Signal Backups, enter the recovery key (or scan the QR code and choose restore from backup, the same thing happened to me for both)
  4. Wait for the restore to download and process all messages

Actual result

Stable (APK from signal.org): after the message import finishes, the "finishing" dialog closes and the app silently returns to the restore-from-cloud-backup screen. logcat shows an uncaught exception and the process being killed:

07-16 14:05:15.209 E SignalUncaughtException: java.lang.IllegalStateException: Foreign key check failed! Violations: [ForeignKeyViolation(table=message, violatingRowId=392184, dependsOnTable=message, column=latest_revision_id), ForeignKeyViolation(table=message, violatingRowId=392188, dependsOnTable=message, column=latest_revision_id), ForeignKeyViolation(table=message, violatingRowId=778878, dependsOnTable=message, column=latest_revision_id), ForeignKeyViolation(table=message, violatingRowId=778881, dependsOnTable=message, column=latest_revision_id), ForeignKeyViolation(table=message, violatingRowId=802186, dependsOnTable=message, column=latest_revision_id), ForeignKeyViolation(table=message, violatingRowId=802190, dependsOnTable=message, column=latest_revision_id)]
07-16 14:05:15.209 E SignalUncaughtException:       at org.thoughtcrime.securesms.backup.v2.BackupRepository.import(BackupRepository.kt:1447)
07-16 14:05:15.209 E SignalUncaughtException:       at org.thoughtcrime.securesms.backup.v2.BackupRepository.importSignalBackup(BackupRepository.kt:1160)
07-16 14:05:15.209 E SignalUncaughtException:       at org.thoughtcrime.securesms.backup.v2.BackupRepository.restoreRemoteBackup(BackupRepository.kt:2306)
07-16 14:05:15.209 E SignalUncaughtException:       at org.thoughtcrime.securesms.backup.v2.BackupRepository.access$restoreRemoteBackup(BackupRepository.kt:188)
07-16 14:05:15.209 E SignalUncaughtException:       at org.thoughtcrime.securesms.backup.v2.BackupRepository$restoreRemoteBackup$2.invokeSuspend(BackupRepository.kt:2210)
07-16 14:05:15.209 E SignalUncaughtException:       at org.signal.registration.ui.restore.RemoteRestoreViewModel$restore$1$2.invokeSuspend(RemoteRestoreViewModel.kt:120)
07-16 14:05:15.224 I Process : Sending signal. PID: 19457 SIG: 9
07-16 14:05:15.302 I Zygote  : Process 19457 exited due to signal 9 (Killed)

While the import is running there are also repeated warnings like this, if it's relevant:

07-16 14:00:02.370 W SQLiteConnectionPool: The connection pool for database '/data/user/0/org.thoughtcrime.securesms/databases/signal.db' has been unable to grant a connection to thread 116 (JobRunner-Core-3) with flags 0x2 for 30.000002 seconds.

Newest pre-release from GitHub: same backup, same failure at the same point (around 7 minutes in, right after the message import), but the error is now caught and an error dialog is shown instead of the process crashing. The failure surfaces from RemoteBackupRestoreView / BackupRepository.importSignalBackup(BackupRepository.kt:1162). So the silent-failure part got fixed at some point, but the restore still fails.

Expected result

The restore completes. Ideally the importer would repair or drop the ~6 orphaned latest_revision_id references (or the iOS exporter wouldn't emit them) rather than discarding an entire 800k-message restore over them. iOS -> Android restores are documented as supported, so this backup should be importable.

Screenshots

No response

Device

Pixel 9 and iPhone 11

Android version

17 (Stock OS & GrapheneOS -- attached logs are from stock)

Signal version

8.19.2

Link to debug log

Full logcat captures from both builds are attached (stable-apk-logcat.txt and prerelease-apk-logcat.txt). Lines containing my home Wi-Fi network's SSID/BSSID/IP have been removed for privacy; nothing Signal-related was removed. Let me know if there's any other logs I should provide too.

stable-apk-logcat.txt
prerelease-apk-logcat.txt

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions