MuntashirAkon/AppManager · error · BackupException

Failed to restore ownership info for index

Error message

Failed to restore ownership info for index ${index}.

What it means

Thrown during restoreDirectory when the `chown -R uid:gid` command run on the restored directory's files fails. AppManager restores file ownership after extracting backup data so the app's files have the correct Linux UID/GID; if the chown command reports failure, this BackupException is raised. On Robolectric unit tests the failure is deliberately ignored.

Solutions

  1. Ensure the device is rooted and AppManager is granted root (su) permission before restoring
  2. Verify dataSourceFile.getFilePath() exists and is on a file system that supports ownership changes (not FUSE/external storage)
  3. Check the chown target uid/gid pair matches the app's current UID on this device (UIDs may differ between devices)
  4. If this is a test environment, confirm Utils.isRoboUnitTest() returns true so the failure is tolerated

Example fix

// before
if (!Runner.runCommand(String.format(Locale.ROOT, "chown -R %d:%d \"%s\"", uidGidPair.uid, uidGidPair.gid, dataSourceFile.getFilePath())).isSuccessful()) {
    throw new BackupException("Failed to restore ownership info for index " + index + ".");
}
// after
int uid = Process.myUid(); // or look up the app's current uid via PackageManager
if (!Runner.runCommand(String.format(Locale.ROOT, "chown -R %d:%d \"%s\"", uid, gid, dataSourceFile.getFilePath())).isSuccessful()) {
    Log.w(TAG, "chown failed; continuing without ownership restore for " + index);
}
Defensive patterns

Strategy: try-catch

Validate before calling

Process suProcess = Runtime.getRuntime().exec("su -c id");
if (suProcess.waitFor() != 0) throw new IllegalStateException("Root not available; ownership restore will fail");

Try / catch

try {
    restoreOp.runRestore();
} catch (BackupException e) {
    if (e.getMessage().contains("Failed to restore ownership info")) {
        Log.w(TAG, "Non-fatal: ownership not restored (no root or unsupported fs)", e);
    } else throw e;
}

Prevention

When it happens

Trigger: Running `chown -R <uid>:<gid> <path>` via Runner.runCommand fails after data extraction, e.g. the process lacks root privileges, the target directory no longer exists, or the shell command returns non-success.

Common situations: Restoring a backup on a non-rooted device or in an environment where su is unavailable; file system (e.g. external storage/FUSE mount) that does not support chown; path containing characters that break the shell quoting; running under an emulator/test harness with a different uid mapping.

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/269c25bdd2df32cd. Report an issue: GitHub.

Appendix: source

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

        }
        // Decrypt data
        try {
            dataFiles = mBackupItem.decrypt(dataFiles);
        } catch (IOException e) {
            throw new BackupException("Failed to decrypt " + Arrays.toString(dataFiles), e);
        }
        // Extract data to the data directory
        try {
            String publicSourceDir = new File(Objects.requireNonNull(mPackageInfo.applicationInfo).publicSourceDir).getParent();
            TarUtils.extract(mBackupInfo.tarType, dataFiles, dataSourceFile, null, BackupUtils
                    .getExcludeDirs(!mRequestedFlags.backupCache(), null), publicSourceDir);
        } catch (Throwable th) {
            throw new BackupException("Failed to restore data files for index " + index + ".", th);
        }
        // Restore UID and GID
        if (!Runner.runCommand(String.format(Locale.ROOT, "chown -R %d:%d \"%s\"", uidGidPair.uid, uidGidPair.gid, dataSourceFile.getFilePath())).isSuccessful()) {
            if (!Utils.isRoboUnitTest()) {
                throw new BackupException("Failed to restore ownership info for index " + index + ".");
            } // else Don't care about permissions
        }
        // Restore context
        if (!dataDirectoryInfo.isExternal()) {
            Runner.runCommand(new String[]{"restorecon", "-R", dataSourceFile.getFilePath()});
        }
    }

    private void restoreAdb(int index) throws BackupException {
        Path[] dataFiles = mBackupItem.getDataFiles(index);
        if (dataFiles.length != 1) {
            throw new BackupException("ADB restore is requested but there are no .ab files.");
        }
        // Decrypt data
        try {
            dataFiles = mBackupItem.decrypt(dataFiles);
        } catch (IOException e) {
            throw new BackupException("Failed to decrypt " + Arrays.toString(dataFiles), e);

View on GitHub (pinned to 0152f468fc)