MuntashirAkon/AppManager · error · BackupException

An uninstallation was necessary but couldn't perform it.

Error message

An uninstallation was necessary but couldn't perform it.

What it means

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.

Source

Thrown at app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java:316

        if (!mRequestedFlags.skipSignatureCheck()) {
            String checksum;
            for (Path file : backupSourceFiles) {
                checksum = DigestUtils.getHexDigest(mBackupInfo.checksumAlgo, file);
                if (!checksum.equals(mChecksum.get(file.getName()))) {
                    throw new BackupException("Source file verification failed." +
                            "\nFile: " + file +
                            "\nFound: " + checksum +
                            "\nRequired: " + mChecksum.get(file.getName()));
                }
            }
        }
        if (!isVerified) {
            // Signature verification failed but still here because signature check is disabled.
            // The only way to restore is to reinstall the app
            synchronized (sLock) {
                PackageInstallerCompat installer = PackageInstallerCompat.getNewInstance();
                if (installer.uninstall(mPackageName, mUserId, false)) {
                    throw new BackupException("An uninstallation was necessary but couldn't perform it.");
                }
            }
        }
        // Setup package staging directory
        Path packageStagingDirectory = Paths.get(PackageUtils.PACKAGE_STAGING_DIRECTORY);
        try {
            synchronized (sLock) {
                PackageUtils.ensurePackageStagingDirectoryPrivileged();
            }
        } catch (Exception ignore) {
        }
        try {
            if (!packageStagingDirectory.canWrite()) {
                packageStagingDirectory = mBackupItem.getUnencryptedBackupPath();
            }
        } catch (IOException e) {
            throw new BackupException("Could not create package staging directory", e);
        }

View on GitHub (pinned to 0152f468fc)

Solutions

  1. Grant App Manager sufficient privileges (root or grant ADB/owner permissions) so it can uninstall packages.
  2. Manually uninstall the app first, then run the restore so no forced uninstall is needed.
  3. Remove device-admin/active device policies or work-profile restrictions on the target app.
  4. If the app is a system app, uninstall only its updates manually or use a different restore strategy (skip APK restore).
Defensive patterns

Strategy: try-catch

Validate before calling

// Before restore, ensure the installer can uninstall the target:
PackageInstallerCompat installer = PackageInstallerCompat.getNewInstance();
// confirm privileges (root/adb) and that the app is not device-admin/system-locked
boolean canUninstall = hasSufficientPrivileges() && !isDeviceAdmin(packageName, userId);

Try / catch

try { restoreOp.runRestore(); } catch (BackupException e) {
    if (e.getMessage().contains("An uninstallation was necessary but couldn't perform it")) {
        // guide user to grant privileges or uninstall the app manually, then retry
    }
}

Prevention

When it happens

Trigger: isVerified == false (cert mismatch, signature check skipped), then installer.uninstall(mPackageName, mUserId, false) returns false during restore.

Common situations: 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.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


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