MuntashirAkon/AppManager · error · BackupException

Could not finalise backup.

Error message

Could not finalise backup.

What it means

The final step of runBackup() replaces the previous backup with the newly assembled one via mBackupItem.commit(); an IOException there is wrapped in this BackupException. All backup content was created (and encrypted) but the atomic swap into the final backup directory failed, leaving the old backup untouched.

Solutions

  1. Check free space and mount status of the backup volume.
  2. Ensure the old backup directory isn't open/locked by another process (e.g. a file manager over SAF).
  3. Remove the stale previous backup manually and retry.
  4. Back up to a local storage path rather than cross-device SAF locations.

Example fix

// before
backupPath = new UriPath("content://com.android.externalstorage.document/tree/primary.../backup"); // slow SAF
// after
backupPath = new LocalPath("/sdcard/AppManager/backup"); // direct FS, commit works
Defensive patterns

Strategy: try-catch

Validate before calling

if (!backupDest.canWrite() || backupDest.getUsableSpace() < 100*1024*1024) { throw new IOException("Cannot finalise: destination unusable"); }

Try / catch

try {
    backupOp.runBackup(progress);
} catch (BackupException e) {
    if (e.getMessage().equals("Could not finalise backup.")) {
        // old backup still intact; clean staged files and retry
        deleteStagedBackupFiles();
    }
}

Prevention

When it happens

Trigger: mBackupItem.commit() throws IOException while renaming/moving the staged backup files into the committed backup location.

Common situations: Destination filesystem doesn't support atomic rename across mount points; storage full; the existing backup directory is locked or on read-only media; SAF provider refused the move.

Understand the failure class

Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.

Related errors


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/f6003aaa8a74be9b. Report an issue: GitHub.

Appendix: source

Thrown at app/src/main/java/io/github/muntashirakon/AppManager/backup/BackupOp.java:206

                Map<String, String> filenameChecksumMap = MetadataManager.writeMetadata(mMetadata, mBackupItem);
                for (Map.Entry<String, String> entry : filenameChecksumMap.entrySet()) {
                    mChecksum.add(entry.getKey(), entry.getValue());
                }
            } catch (IOException e) {
                throw new BackupException("Failed to write metadata.", e);
            }
            mChecksum.close();
            // Encrypt checksum
            try {
                mBackupItem.encrypt(new Path[]{mChecksum.getFile()});
            } catch (IOException e) {
                throw new BackupException("Failed to write checksums.txt", e);
            }
            // Replace current backup
            try {
                mBackupItem.commit();
            } catch (IOException e) {
                throw new BackupException("Could not finalise backup.", e);
            }
        } catch (BackupException e) {
            throw e;
        } catch (Throwable th) {
            throw new BackupException("Unknown error occurred.", th);
        }
    }

    private static void incrementProgress(@Nullable ProgressHandler progressHandler) {
        if (progressHandler == null) {
            return;
        }
        float current = progressHandler.getLastProgress() + 1;
        progressHandler.postUpdate(current);
    }

    public BackupMetadataV5 setupMetadataAndCrypto() throws CryptoException {
        // We don't need to backup custom users or multiple backup flags

View on GitHub (pinned to 0152f468fc)