{"record":{"id":"47cc3085380e8e72","repo":"MuntashirAkon/AppManager","slug":"failed-to-decrypt-arrays-tostring-datafiles","errorCode":null,"errorMessage":"Failed to decrypt ${Arrays.toString(dataFiles)}","messagePattern":"Failed to decrypt (.+?)","errorType":"exception","errorClass":"BackupException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java","lineNumber":581,"sourceCode":"        if (!dataSourceFile.exists()) {\n            if (dataDirectoryInfo.isExternal() && !dataDirectoryInfo.isMounted) {\n                if (!Utils.isRoboUnitTest()) {\n                    throw new BackupException(\"External directory containing \" + dataSource + \" is not mounted.\");\n                } // else Skip checking for mounted partition for robolectric tests\n            }\n            if (!dataSourceFile.mkdirs()) {\n                throw new BackupException(\"Could not create directory \" + dataSourceFile);\n            }\n            if (!dataDirectoryInfo.isExternal()) {\n                // Restore UID, GID\n                dataSourceFile.setUidGid(uidGidPair);\n            }\n        }\n        // Decrypt data\n        try {\n            dataFiles = mBackupItem.decrypt(dataFiles);\n        } catch (IOException e) {\n            throw new BackupException(\"Failed to decrypt \" + Arrays.toString(dataFiles), e);\n        }\n        // Extract data to the data directory\n        try {\n            String publicSourceDir = new File(Objects.requireNonNull(mPackageInfo.applicationInfo).publicSourceDir).getParent();\n            TarUtils.extract(mBackupInfo.tarType, dataFiles, dataSourceFile, null, BackupUtils\n                    .getExcludeDirs(!mRequestedFlags.backupCache(), null), publicSourceDir);\n        } catch (Throwable th) {\n            throw new BackupException(\"Failed to restore data files for index \" + index + \".\", th);\n        }\n        // Restore UID and GID\n        if (!Runner.runCommand(String.format(Locale.ROOT, \"chown -R %d:%d \\\"%s\\\"\", uidGidPair.uid, uidGidPair.gid, dataSourceFile.getFilePath())).isSuccessful()) {\n            if (!Utils.isRoboUnitTest()) {\n                throw new BackupException(\"Failed to restore ownership info for index \" + index + \".\");\n            } // else Don't care about permissions\n        }\n        // Restore context\n        if (!dataDirectoryInfo.isExternal()) {\n            Runner.runCommand(new String[]{\"restorecon\", \"-R\", dataSourceFile.getFilePath()});","sourceCodeStart":563,"sourceCodeEnd":599,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java#L563-L599","documentation":"Before extraction, restoreDirectory calls mBackupItem.decrypt(dataFiles) to decrypt encrypted backup archives; an IOException here is wrapped in this BackupException listing the files passed in. It means the backup is encrypted (crypto info present) but the archives could not be decrypted — usually a wrong password or corrupt ciphertext.","triggerScenarios":"Wrong decryption password/key supplied in the restore options; backup encrypted with an algorithm/key that doesn't match the provided credentials; the encrypted files were corrupted or truncated; crypto metadata (backup.crypto) missing or tampered with while files remain encrypted.","commonSituations":"User forgets the password chosen at backup time; copy-pasting password with trailing whitespace; backups decrypted on another device where the key file wasn't carried over; partially synced encrypted archives.","solutions":["Re-enter the correct encryption password/key used when the backup was created.","Check the password for trailing whitespace or encoding issues; retype it.","Re-copy the encrypted archives and the .crypto metadata from the original backup source.","Restore a non-encrypted backup, or re-create the backup with a known password.","Inspect the wrapped IOException message to confirm it is a decryption (bad padding/MAC) failure vs. a file-read failure."],"exampleFix":"// before: restore with wrong password\noptions.setPassword(wrongPass);\nnew RestoreOp(...).runRestore(); // BackupException: failed to decrypt\n// after: prompt until correct\nwhile (true) {\n    options.setPassword(promptPassword());\n    try { runRestore(); break; }\n    catch (BackupException e) { if (!e.getMessage().contains(\"decrypt\")) throw e; }\n}","handlingStrategy":"try-catch","validationCode":"// Ensure credentials are present and non-blank before encrypted restore\nif (options.isEncrypted() && (options.getPassword() == null || options.getPassword().trim().isEmpty()))\n    throw new IllegalStateException(\"Password required for encrypted backup\");\n","typeGuard":"boolean hasDecryptionCredentials(RestoreOptions o) { return !o.isEncrypted() || (o.getPassword() != null && !o.getPassword().isEmpty()); }","tryCatchPattern":"try { runRestore(); } catch (BackupException e) {\n    if (e.getMessage().contains(\"Failed to decrypt\")) {\n        if (e.getCause() instanceof IOException) promptForPasswordAgain();\n    } else throw e;\n}","preventionTips":["Store the backup password in a password manager at backup time.","Copy the .crypto metadata file together with encrypted archives.","Trim whitespace/encoding issues from passwords before use.","Test-decrypt a small backup before wiping the source device."],"tags":["android","backup","encryption","decryption","password"],"backgroundTag":"missing-credentials","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T16:17:12.679Z"}