MuntashirAkon/AppManager · error · BackupException

Failed to restore ownership info for index ${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.

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)

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.