{"record":{"id":"740a45db17d929a4","repo":"MuntashirAkon/AppManager","slug":"archive-is-encrypted-but-no-password-given","errorCode":null,"errorMessage":"Archive is encrypted but no password given","messagePattern":"Archive is encrypted but no password given","errorType":"exception","errorClass":"IOException","httpStatus":null,"severity":"error","filePath":"app/src/main/java/io/github/muntashirakon/AppManager/backup/adb/AndroidBackupHeader.java","lineNumber":93,"sourceCode":"        byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes(StandardCharsets.UTF_8);\n        if (Arrays.equals(magicBytes, streamHeader)) {\n            // okay, header looks good.  now parse out the rest of the fields.\n            String s = readHeaderLine(backupStream);\n            mBackupFileVersion = Integer.parseInt(s);\n            if (mBackupFileVersion <= BACKUP_FILE_VERSION) {\n                // okay, it's a version we recognize.  if it's version 1, we may need\n                // to try two different PBKDF2 regimes to compare checksums.\n                final boolean pbkdf2Fallback = (mBackupFileVersion == 1);\n\n                s = readHeaderLine(backupStream);\n                mCompress = (Integer.parseInt(s) != 0);\n                s = readHeaderLine(backupStream);\n                if (s.equals(\"none\")) {\n                    // no more header to parse; we're good to go\n                } else if (mPassword != null && mPassword.length > 0) { // AES-256\n                    preCompressStream = decodeAesHeaderAndInitialize(mPassword, s, pbkdf2Fallback, backupStream);\n                } else {\n                    throw new IOException(\"Archive is encrypted but no password given\");\n                }\n            } else {\n                throw new IOException(\"Wrong header version: \" + s);\n            }\n        } else {\n            throw new IOException(\"Didn't read the right header magic\");\n        }\n\n        // okay, use the right stream layer based on compression\n        return mCompress ? new InflaterInputStream(preCompressStream) : preCompressStream;\n    }\n\n    @NonNull\n    public OutputStream write(@NonNull OutputStream backupStream) throws Exception {\n        // Write the global file header.  All strings are UTF-8 encoded; lines end\n        // with a '\\n' byte.  Actual backup data begins immediately following the\n        // final '\\n'.\n        //","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/MuntashirAkon/AppManager/blob/0152f468fc9463ee02dc2ca83f6fe4989a2c4ca5/app/src/main/java/io/github/muntashirakon/AppManager/backup/adb/AndroidBackupHeader.java#L75-L111","documentation":"IOException from AndroidBackupHeader.read: the header declares an encryption algorithm, but no password (or an empty one) was supplied, so the AES-256 encrypted payload cannot be decrypted. The library refuses to continue rather than produce garbage output.","triggerScenarios":"Calling toTar()/read() on an encrypted .ab backup while the extractor/header was constructed with a null or empty char[] password.","commonSituations":"Restoring an adb backup created with 'adb backup -apk -nosystem ...' where a password was typed, but the restore code path wasn't given one; password lost/omitted in automated restore scripts.","solutions":["Supply the backup password via the password parameter (char[]) when constructing the extractor/header","Re-ask the user for the password and pass it non-empty","If the password is unknown, the archive cannot be restored — create a new backup","Confirm the archive really is yours; encrypted backups without the password are unrecoverable by design"],"exampleFix":"// before\nAndroidBackupHeader header = new AndroidBackupHeader(backupStream, null);\nInputStream tar = header.toTar();\n// after\nchar[] password = promptForPassword(); // non-empty\nAndroidBackupHeader header = new AndroidBackupHeader(backupStream, password);\nInputStream tar = header.toTar();","handlingStrategy":"try-catch","validationCode":"// Detect encryption requirement from header before parsing\nString encLine = peekHeaderLine(backupFile, 3); // 0-based: magic, version, encryption\nboolean encrypted = !\"none\".equals(encLine);\nif (encrypted && (password == null || password.length == 0)) {\n    throw new IllegalArgumentException(\"Backup is encrypted; password required\");\n}","typeGuard":null,"tryCatchPattern":"try {\n    InputStream tar = header.toTar();\n} catch (IOException e) {\n    if (e.getMessage().contains(\"no password given\")) {\n        char[] pw = promptUserForPassword();\n        header = new AndroidBackupHeader(reopen(backupFile), pw);\n        tar = header.toTar();\n    } else throw e;\n}","preventionTips":["Always prompt for a password when the header encryption line != 'none'","Pass char[] not String for passwords, and null it after use","Record whether backups were created encrypted","Handle 'none' explicitly in restore scripts"],"tags":["backup","encryption","missing-password"],"backgroundTag":"missing-credentials","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"}