{"record":{"id":"4f8ba764b9ae23e7","repo":"MuntashirAkon/AppManager","slug":"an-uninstallation-was-necessary-but-couldn-t-perform-it","errorCode":null,"errorMessage":"An uninstallation was necessary but couldn't perform it.","messagePattern":"An uninstallation was necessary but couldn't perform it\\.","errorType":"exception","errorClass":"BackupException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java","lineNumber":316,"sourceCode":"        if (!mRequestedFlags.skipSignatureCheck()) {\n            String checksum;\n            for (Path file : backupSourceFiles) {\n                checksum = DigestUtils.getHexDigest(mBackupInfo.checksumAlgo, file);\n                if (!checksum.equals(mChecksum.get(file.getName()))) {\n                    throw new BackupException(\"Source file verification failed.\" +\n                            \"\\nFile: \" + file +\n                            \"\\nFound: \" + checksum +\n                            \"\\nRequired: \" + mChecksum.get(file.getName()));\n                }\n            }\n        }\n        if (!isVerified) {\n            // Signature verification failed but still here because signature check is disabled.\n            // The only way to restore is to reinstall the app\n            synchronized (sLock) {\n                PackageInstallerCompat installer = PackageInstallerCompat.getNewInstance();\n                if (installer.uninstall(mPackageName, mUserId, false)) {\n                    throw new BackupException(\"An uninstallation was necessary but couldn't perform it.\");\n                }\n            }\n        }\n        // Setup package staging directory\n        Path packageStagingDirectory = Paths.get(PackageUtils.PACKAGE_STAGING_DIRECTORY);\n        try {\n            synchronized (sLock) {\n                PackageUtils.ensurePackageStagingDirectoryPrivileged();\n            }\n        } catch (Exception ignore) {\n        }\n        try {\n            if (!packageStagingDirectory.canWrite()) {\n                packageStagingDirectory = mBackupItem.getUnencryptedBackupPath();\n            }\n        } catch (IOException e) {\n            throw new BackupException(\"Could not create package staging directory\", e);\n        }","sourceCodeStart":298,"sourceCodeEnd":334,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java#L298-L334","documentation":"When signature verification failed but checks were skipped, restoreApkFiles() must uninstall the currently installed (differently-signed) app before installing the backed-up APK. If PackageInstallerCompat.uninstall() returns false (the uninstall did not succeed), the restore cannot proceed safely and this BackupException is thrown.","triggerScenarios":"isVerified == false (cert mismatch, signature check skipped), then installer.uninstall(mPackageName, mUserId, false) returns false during restore.","commonSituations":"App Manager lacks the permission/rights to uninstall (non-root without sufficient ADB privileges, device-owner restrictions); the app is a device admin or has active device policies; the app is a system app that cannot be fully uninstalled; user or work-profile restrictions block uninstallation.","solutions":["Grant App Manager sufficient privileges (root or grant ADB/owner permissions) so it can uninstall packages.","Manually uninstall the app first, then run the restore so no forced uninstall is needed.","Remove device-admin/active device policies or work-profile restrictions on the target app.","If the app is a system app, uninstall only its updates manually or use a different restore strategy (skip APK restore)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"// Before restore, ensure the installer can uninstall the target:\nPackageInstallerCompat installer = PackageInstallerCompat.getNewInstance();\n// confirm privileges (root/adb) and that the app is not device-admin/system-locked\nboolean canUninstall = hasSufficientPrivileges() && !isDeviceAdmin(packageName, userId);","typeGuard":null,"tryCatchPattern":"try { restoreOp.runRestore(); } catch (BackupException e) {\n    if (e.getMessage().contains(\"An uninstallation was necessary but couldn't perform it\")) {\n        // guide user to grant privileges or uninstall the app manually, then retry\n    }\n}","preventionTips":["Set up App Manager with root or ADB grants before performing signature-mismatched restores.","Uninstall manually any differently-signed version ahead of the restore.","Clear device-admin/policy restrictions on apps slated for restore."],"tags":["android","package-installer","backup","permissions"],"backgroundTag":"permission-denied","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"}