{"record":{"id":"9e9c05dfc52c258a","repo":"MuntashirAkon/AppManager","slug":"couldn-t-verify-permission-file-nfile-rulesfile-nfound","errorCode":null,"errorMessage":"Couldn't verify permission file.\\nFile: ${rulesFile}\\nFound: ${checksum}\\nRequired: ${mChecksum.get(rulesFile.getName())}","messagePattern":"Couldn't verify permission file\\.\\\\nFile: (.+?)\\\\nFound: (.+?)\\\\nRequired: (.+?)","errorType":"exception","errorClass":"BackupException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java","lineNumber":771,"sourceCode":"        // Apply rules\n        if (!mIsInstalled) {\n            throw new BackupException(\"Rules restore is requested but the app isn't installed.\");\n        }\n        Path rulesFile;\n        try {\n            rulesFile = mBackupItem.getRulesFile();\n        } catch (IOException e) {\n            if (mBackupMetadata.hasRules) {\n                throw new BackupException(\"Rules file is missing.\", e);\n            } else {\n                // There are no rules, just skip\n                return;\n            }\n        }\n        if (!mRequestedFlags.skipSignatureCheck()) {\n            String checksum = DigestUtils.getHexDigest(mBackupInfo.checksumAlgo, rulesFile);\n            if (!checksum.equals(mChecksum.get(rulesFile.getName()))) {\n                throw new BackupException(\"Couldn't verify permission file.\" +\n                        \"\\nFile: \" + rulesFile +\n                        \"\\nFound: \" + checksum +\n                        \"\\nRequired: \" + mChecksum.get(rulesFile.getName()));\n            }\n        }\n        // Decrypt rules file\n        try {\n            rulesFile = mBackupItem.decrypt(new Path[]{rulesFile})[0];\n        } catch (IOException | IndexOutOfBoundsException e) {\n            throw new BackupException(\"Failed to decrypt \" + rulesFile.getName(), e);\n        }\n        try (RulesImporter importer = new RulesImporter(Arrays.asList(RuleType.values()), new int[]{mUserId})) {\n            importer.addRulesFromPath(rulesFile);\n            importer.setPackagesToImport(Collections.singletonList(mPackageName));\n            importer.applyRules(true);\n        } catch (IOException e) {\n            throw new BackupException(\"Failed to restore rules file.\", e);\n        }","sourceCodeStart":753,"sourceCodeEnd":789,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java#L753-L789","documentation":"RestoreOp.restoreRules throws this BackupException when the on-disk permission/rules file's checksum (computed with the backup's recorded algorithm) does not match the checksum stored in the backup's checksum map. It is an integrity check to detect a corrupted or tampered rules file during restore, and can be bypassed only with the skipSignatureCheck flag.","triggerScenarios":"Calling runRestore with backupRules enabled while the rules file (e.g. permissions.xml or rules JSON) was modified after backup, partially downloaded/copied, or re-encoded with different formatting/line endings so the digest differs from mChecksum.","commonSituations":"Backups transferred over unreliable channels (MTP, cloud sync truncating files), files edited manually on device, restoring a backup taken by a different AppManager version that serialized rules differently, or a storage corruption on the backup media.","solutions":["Recopy or re-download the backup from a trusted source so the rules file matches the recorded checksum.","Take a fresh backup of the app so a new rules file and matching checksum are generated.","If you accept the risk, re-run restore with the skip-signature-check flag so the checksum comparison is skipped.","Verify the checksum algorithm support: if the backup came from an older version, ensure DigestUtils supports the recorded checksumAlgo."],"exampleFix":"// before\nam --backup-restore restore <backup>\n// after (skip integrity check deliberately)\nam --backup-restore restore <backup> --skip-signature-check","handlingStrategy":"validation","validationCode":"// Verify the rules file digest before starting restore\nString algo = backupInfo.checksumAlgo;\nString actual = DigestUtils.getHexDigest(algo, rulesFile);\nString expected = checksumMap.get(new File(rulesFile).getName());\nboolean ok = actual != null && actual.equals(expected);","typeGuard":null,"tryCatchPattern":"try {\n    restoreOp.runRestore();\n} catch (BackupException e) {\n    if (e.getMessage().startsWith(\"Couldn't verify permission file.\")) {\n        // prompt user to re-copy backup or re-run with skipSignatureCheck\n    }\n}","preventionTips":["Always transfer backups with integrity-preserving channels (adb pull/push, checksumming sync tools).","Never hand-edit files inside a backup directory.","Keep a manifest of backup file hashes at backup time."],"tags":["checksum","integrity","backup-restore","file-verification"],"backgroundTag":"checksum-mismatch","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}