{"record":{"id":"68e12527cca4eec5","repo":"MuntashirAkon/AppManager","slug":"unsupported-file-in-ab-filename","errorCode":null,"errorMessage":"Unsupported file in AB: \" + filename","messagePattern":"Unsupported file in AB: \" \\+ filename","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/backup/adb/AndroidBackupExtractor.java","lineNumber":81,"sourceCode":"        mFilesToBeDeleted.add(tarFile);\n        Path dest = temporaryDir.createNewDirectory(abFilename);\n        mFilesToBeDeleted.add(dest);\n        toTar(abFile, tarFile, null);\n        try (InputStream fis = tarFile.openInputStream();\n             TarArchiveInputStream tis = new TarArchiveInputStream(fis)) {\n            String realDestPath = dest.getRealFilePath();\n            int relDirSize = relativeDirInAb.length();\n            TarArchiveEntry entry;\n            while ((entry = tis.getNextTarEntry()) != null) {\n                String filename = Paths.normalize(entry.getName());\n                // Early zip slip vulnerability check to avoid creating any files at all\n                if (filename == null || filename.startsWith(\"../\")) {\n                    throw new IOException(\"Zip slip vulnerability detected!\" +\n                            \"\\nExpected dest: \" + new File(realDestPath, entry.getName()) +\n                            \"\\nActual path: \" + (filename != null ? new File(realDestPath, filename) : realDestPath));\n                }\n                if (!filename.startsWith(relativeDirInAb)) {\n                    throw new IOException(\"Unsupported file in AB: \" + filename);\n                }\n                // Remove apps/{packageName}/ part\n                filename = filename.substring(relDirSize);\n                Path file;\n                if (entry.isDirectory()) {\n                    file = dest.createDirectoriesIfRequired(filename);\n                } else file = dest.createNewArbitraryFile(filename, null);\n                // Check if the given entry is a link.\n                if (entry.isSymbolicLink() && file.getFilePath() != null) {\n                    String linkName = entry.getLinkName();\n                    file.delete();\n                    file.createNewSymbolicLink(linkName);\n                } else {\n                    // Zip slip vulnerability might still be present\n                    String realFilePath = file.getRealFilePath();\n                    if (realDestPath != null && realFilePath != null && !realFilePath.startsWith(realDestPath)) {\n                        throw new IOException(\"Zip slip vulnerability detected!\" +\n                                \"\\nExpected dest: \" + new File(realDestPath, entry.getName()) +","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/backup/adb/AndroidBackupExtractor.java#L63-L99","documentation":"IOException thrown when a TAR entry from the Android backup stream, after zip-slip checks, does not fall under the expected apps/{packageName}/ prefix (relativeDirInAb). The extractor only supports entries belonging to the package being restored; anything else is rejected.","triggerScenarios":"An .ab archive whose entry paths are outside apps/<packageName>/, e.g. entries for a different package, absolute-style paths, or a mismatch between the package name the extractor was created with and the package actually contained in the archive.","commonSituations":"Restoring a backup of one package into another's restore session; hand-assembled or tool-generated .ab files with inconsistent directory layout; adb backup that included multiple apps but a single-app extractor is used.","solutions":["Ensure the package name used to configure the extractor matches the package inside the archive","Restore each package from its own backup file rather than a multi-app archive","Inspect entry names in the .ab (converted to TAR) to confirm the apps/<pkg>/ layout","Regenerate the backup so entries live under the expected relative directory"],"exampleFix":"// before\nAndroidBackupExtractor ex = new AndroidBackupExtractor(abFile, destDir, \"com.example.other\", false);\nex.extract();\n// after (match package to archive contents)\nString pkgInArchive = detectPackageName(abFile); // first apps/ segment\nif (!pkgInArchive.equals(\"com.example.other\")) {\n    throw new IOException(\"Archive holds \" + pkgInArchive + \", not the requested package\");\n}\nAndroidBackupExtractor ex = new AndroidBackupExtractor(abFile, destDir, pkgInArchive, false);\nex.extract();","handlingStrategy":"try-catch","validationCode":"// Confirm archive package matches expected before extraction\nString pkg = detectFirstAppsSegment(abFile);\nif (!expectedPkg.equals(pkg)) throw new IOException(\"Package mismatch: \" + pkg);","typeGuard":null,"tryCatchPattern":"try {\n    extractor.extract();\n} catch (IOException e) {\n    if (e.getMessage().startsWith(\"Unsupported file in AB\")) {\n        Log.e(TAG, \"Archive layout does not match expected package\", e);\n    } else throw e;\n}","preventionTips":["Match the extractor's package argument to the archive contents","Restore single-app backups individually","Use the same tool version for backup and restore","Inspect entry paths after converting .ab to TAR when debugging"],"tags":["backup","tar","unexpected-entry"],"backgroundTag":"unexpected-response-shape","analyzedSha":"0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5","analyzedAt":"2026-09-12T14:03:37.243Z","contentChangedAt":"2026-09-12T14:03:37.243Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}