{"record":{"id":"269c25bdd2df32cd","repo":"MuntashirAkon/AppManager","slug":"failed-to-restore-ownership-info-for-index-index","errorCode":null,"errorMessage":"Failed to restore ownership info for index ${index}.","messagePattern":"Failed to restore ownership info for index (.+?)\\.","errorType":"exception","errorClass":"BackupException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java","lineNumber":594,"sourceCode":"        }\n        // Decrypt data\n        try {\n            dataFiles = mBackupItem.decrypt(dataFiles);\n        } catch (IOException e) {\n            throw new BackupException(\"Failed to decrypt \" + Arrays.toString(dataFiles), e);\n        }\n        // Extract data to the data directory\n        try {\n            String publicSourceDir = new File(Objects.requireNonNull(mPackageInfo.applicationInfo).publicSourceDir).getParent();\n            TarUtils.extract(mBackupInfo.tarType, dataFiles, dataSourceFile, null, BackupUtils\n                    .getExcludeDirs(!mRequestedFlags.backupCache(), null), publicSourceDir);\n        } catch (Throwable th) {\n            throw new BackupException(\"Failed to restore data files for index \" + index + \".\", th);\n        }\n        // Restore UID and GID\n        if (!Runner.runCommand(String.format(Locale.ROOT, \"chown -R %d:%d \\\"%s\\\"\", uidGidPair.uid, uidGidPair.gid, dataSourceFile.getFilePath())).isSuccessful()) {\n            if (!Utils.isRoboUnitTest()) {\n                throw new BackupException(\"Failed to restore ownership info for index \" + index + \".\");\n            } // else Don't care about permissions\n        }\n        // Restore context\n        if (!dataDirectoryInfo.isExternal()) {\n            Runner.runCommand(new String[]{\"restorecon\", \"-R\", dataSourceFile.getFilePath()});\n        }\n    }\n\n    private void restoreAdb(int index) throws BackupException {\n        Path[] dataFiles = mBackupItem.getDataFiles(index);\n        if (dataFiles.length != 1) {\n            throw new BackupException(\"ADB restore is requested but there are no .ab files.\");\n        }\n        // Decrypt data\n        try {\n            dataFiles = mBackupItem.decrypt(dataFiles);\n        } catch (IOException e) {\n            throw new BackupException(\"Failed to decrypt \" + Arrays.toString(dataFiles), e);","sourceCodeStart":576,"sourceCodeEnd":612,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/backup/RestoreOp.java#L576-L612","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before\nif (!Runner.runCommand(String.format(Locale.ROOT, \"chown -R %d:%d \\\"%s\\\"\", uidGidPair.uid, uidGidPair.gid, dataSourceFile.getFilePath())).isSuccessful()) {\n    throw new BackupException(\"Failed to restore ownership info for index \" + index + \".\");\n}\n// after\nint uid = Process.myUid(); // or look up the app's current uid via PackageManager\nif (!Runner.runCommand(String.format(Locale.ROOT, \"chown -R %d:%d \\\"%s\\\"\", uid, gid, dataSourceFile.getFilePath())).isSuccessful()) {\n    Log.w(TAG, \"chown failed; continuing without ownership restore for \" + index);\n}","handlingStrategy":"try-catch","validationCode":"Process suProcess = Runtime.getRuntime().exec(\"su -c id\");\nif (suProcess.waitFor() != 0) throw new IllegalStateException(\"Root not available; ownership restore will fail\");","typeGuard":null,"tryCatchPattern":"try {\n    restoreOp.runRestore();\n} catch (BackupException e) {\n    if (e.getMessage().contains(\"Failed to restore ownership info\")) {\n        Log.w(TAG, \"Non-fatal: ownership not restored (no root or unsupported fs)\", e);\n    } else throw e;\n}","preventionTips":["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"],"tags":["android","backup","root","permissions","chown"],"backgroundTag":"permission-denied","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}