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
- Ensure the device is rooted and AppManager is granted root (su) permission before restoring
- Verify dataSourceFile.getFilePath() exists and is on a file system that supports ownership changes (not FUSE/external storage)
- Check the chown target uid/gid pair matches the app's current UID on this device (UIDs may differ between devices)
- 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
- Run restores with root access granted
- Prefer internal storage paths for backup data
- Confirm the app's UID with PackageManager before chown
- Test restores on the same user profile the backup was taken from
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
- Could not delete the selected backups
- An uninstallation was necessary but couldn't perform it.
- Failed to restore the KeyStore files.
- Failed to rename KeyStore files
- Could not create directory ${dataSourceFile}
AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12).
Data as JSON: /api/errors/269c25bdd2df32cd.
Report an issue: GitHub.