{"record":{"id":"1ca9094246e9eba7","repo":"MuntashirAkon/AppManager","slug":"incorrect-number-of-apk-files","errorCode":null,"errorMessage":"Incorrect number of APK files: ","messagePattern":"Incorrect number of APK files: ","errorType":"exception","errorClass":"BackupException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/backup/convert/OABConverter.java","lineNumber":265,"sourceCode":"    }\n\n    private void backupApkFile() throws BackupException {\n        Path[] baseApkFiles;\n        try {\n            baseApkFiles = new Path[]{mBackupLocation.findFile(CryptoUtils.getAppropriateFilename(\n                    mSourceMetadata.apkName, mSourceCryptoMode))};\n        } catch (FileNotFoundException e) {\n            throw new BackupException(\"Could not get base.apk file.\", e);\n        }\n        // Decrypt APK file if needed\n        try {\n            baseApkFiles = ConvertUtils.decryptSourceFiles(baseApkFiles, mSourceCrypto, mSourceCryptoMode, mBackupItem);\n        } catch (IOException e) {\n            throw new BackupException(\"Failed to decrypt \" + Arrays.toString(baseApkFiles), e);\n        }\n        // baseApkFiles should be a singleton array\n        if (baseApkFiles.length != 1) {\n            throw new BackupException(\"Incorrect number of APK files: \" + baseApkFiles.length);\n        }\n        Path baseApkFile = baseApkFiles[0];\n        // Get certificate checksums\n        try {\n            String[] checksums = ConvertUtils.getChecksumsFromApk(baseApkFile, mDestMetadata.info.checksumAlgo);\n            for (int i = 0; i < checksums.length; ++i) {\n                mChecksum.add(CERT_PREFIX + i, checksums[i]);\n            }\n        } catch (Exception ignore) {\n        }\n        // Backup APK file\n        String sourceBackupFilePrefix = BackupUtils.getSourceFilePrefix(getExt(mDestMetadata.info.tarType));\n        Path[] sourceFiles;\n        try {\n            sourceFiles = TarUtils.create(mDestMetadata.info.tarType, baseApkFile, mBackupItem.getUnencryptedBackupPath(), sourceBackupFilePrefix,\n                            /* language=regexp */ new String[]{\".*\\\\.apk\"}, null, null, false)\n                    .toArray(new Path[0]);\n        } catch (Throwable th) {","sourceCodeStart":247,"sourceCodeEnd":283,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/backup/convert/OABConverter.java#L247-L283","documentation":"After decryption, the converter asserts that decryptSourceFiles() returned exactly one APK file (a singleton array), since only one base APK is expected. Any other count means the decryption/normalization step behaved unexpectedly and the subsequent checksum and tar logic would be wrong, so it throws with the actual length. This is an internal invariant check.","triggerScenarios":"In backupApkFile(), baseApkFiles.length != 1 right after ConvertUtils.decryptSourceFiles() at OABConverter.java:265 — e.g. decryption produced 0 or multiple output paths.","commonSituations":"Source file was a split/multi-APK set so decryption yields several files; the decrypted output directory already contained stale files from a previous run; a decrypt implementation change alters the returned array contents.","solutions":["Ensure the source is a single base APK, not a split-APK bundle; convert one APK at a time.","Clean the backup item's temporary/decrypted output directory of stale files and retry.","Verify the decryptSourceFiles() behavior for your crypto mode matches the expected single-output contract.","Re-create the source backup containing exactly one APK file and rerun the conversion."],"exampleFix":"// before: backup dir holds base.apk + split_config.apk fed through one item\n// after: point the converter at a backup containing only base.apk,\n// or split the job per APK:\n// convert(backupDirWithOnlyBaseApk)","handlingStrategy":"validation","validationCode":"// Convert only single-APK sources\nif (countApkFilesInBackup(backupDir) != 1) {\n    throw new IllegalStateException(\"Converter expects exactly one base APK in the source backup\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    converter.convert(...);\n} catch (BackupException e) {\n    if (e.getMessage().startsWith(\"Incorrect number of APK files\")) {\n        Log.e(TAG, \"Decryption returned non-singleton array; clean temp dir and retry\", e);\n    }\n}","preventionTips":["Clean the backup item's temporary/decrypted output directory between runs.","Feed one base APK per conversion job; handle split APKs separately.","Re-verify the source backup contains a single APK before converting."],"tags":["android","backup","invariant","apk","decryption"],"backgroundTag":"internal-invariant-violation","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"}