MuntashirAkon/AppManager · error · IOException

Archive is encrypted but no password given

Error message

Archive is encrypted but no password given

What it means

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.

Solutions

  1. Supply the backup password via the password parameter (char[]) when constructing the extractor/header
  2. Re-ask the user for the password and pass it non-empty
  3. If the password is unknown, the archive cannot be restored — create a new backup
  4. Confirm the archive really is yours; encrypted backups without the password are unrecoverable by design

Example fix

// before
AndroidBackupHeader header = new AndroidBackupHeader(backupStream, null);
InputStream tar = header.toTar();
// after
char[] password = promptForPassword(); // non-empty
AndroidBackupHeader header = new AndroidBackupHeader(backupStream, password);
InputStream tar = header.toTar();
Defensive patterns

Strategy: try-catch

Validate before calling

// Detect encryption requirement from header before parsing
String encLine = peekHeaderLine(backupFile, 3); // 0-based: magic, version, encryption
boolean encrypted = !"none".equals(encLine);
if (encrypted && (password == null || password.length == 0)) {
    throw new IllegalArgumentException("Backup is encrypted; password required");
}

Try / catch

try {
    InputStream tar = header.toTar();
} catch (IOException e) {
    if (e.getMessage().contains("no password given")) {
        char[] pw = promptUserForPassword();
        header = new AndroidBackupHeader(reopen(backupFile), pw);
        tar = header.toTar();
    } else throw e;
}

Prevention

When it happens

Trigger: Calling toTar()/read() on an encrypted .ab backup while the extractor/header was constructed with a null or empty char[] password.

Common situations: 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.

Related errors


AI-assisted analysis of MuntashirAkon/AppManager@0152f468fc (2026-09-12). Data as JSON: /api/errors/740a45db17d929a4. Report an issue: GitHub.

Appendix: source

Thrown at app/src/main/java/io/github/muntashirakon/AppManager/backup/adb/AndroidBackupHeader.java:93

        byte[] magicBytes = BACKUP_FILE_HEADER_MAGIC.getBytes(StandardCharsets.UTF_8);
        if (Arrays.equals(magicBytes, streamHeader)) {
            // okay, header looks good.  now parse out the rest of the fields.
            String s = readHeaderLine(backupStream);
            mBackupFileVersion = Integer.parseInt(s);
            if (mBackupFileVersion <= BACKUP_FILE_VERSION) {
                // okay, it's a version we recognize.  if it's version 1, we may need
                // to try two different PBKDF2 regimes to compare checksums.
                final boolean pbkdf2Fallback = (mBackupFileVersion == 1);

                s = readHeaderLine(backupStream);
                mCompress = (Integer.parseInt(s) != 0);
                s = readHeaderLine(backupStream);
                if (s.equals("none")) {
                    // no more header to parse; we're good to go
                } else if (mPassword != null && mPassword.length > 0) { // AES-256
                    preCompressStream = decodeAesHeaderAndInitialize(mPassword, s, pbkdf2Fallback, backupStream);
                } else {
                    throw new IOException("Archive is encrypted but no password given");
                }
            } else {
                throw new IOException("Wrong header version: " + s);
            }
        } else {
            throw new IOException("Didn't read the right header magic");
        }

        // okay, use the right stream layer based on compression
        return mCompress ? new InflaterInputStream(preCompressStream) : preCompressStream;
    }

    @NonNull
    public OutputStream write(@NonNull OutputStream backupStream) throws Exception {
        // Write the global file header.  All strings are UTF-8 encoded; lines end
        // with a '\n' byte.  Actual backup data begins immediately following the
        // final '\n'.
        //

View on GitHub (pinned to 0152f468fc)