0x192/universal-android-debloater · error

Unable to parse backup file

Error message

Unable to parse backup file

What it means

list_available_backup_user reads a backup JSON file and deserializes it into PhoneBackup with serde_json::from_str(...).expect("Unable to parse backup file"). The panic means the file exists but is not valid JSON or does not match the PhoneBackup schema (users field shape/types).

Source

Thrown at src/core/save.rs:88

    }
}

pub fn list_available_backups(dir: &Path) -> Vec<DisplayablePath> {
    #[allow(clippy::option_if_let_else)]
    match fs::read_dir(dir) {
        Ok(files) => files
            .filter_map(|e| e.ok())
            .map(|e| DisplayablePath { path: e.path() })
            .collect::<Vec<_>>(),
        Err(_) => vec![],
    }
}

pub fn list_available_backup_user(backup: DisplayablePath) -> Vec<User> {
    match fs::read_to_string(backup.path) {
        Ok(data) => {
            let phone_backup: PhoneBackup =
                serde_json::from_str(&data).expect("Unable to parse backup file");

            let mut users = vec![];
            for u in phone_backup.users {
                users.push(User {
                    id: u.id,
                    index: 0,
                    protected: false,
                });
            }
            users
        }
        Err(e) => {
            error!("[BACKUP]: Selected backup file not found: {}", e);
            vec![]
        }
    }
}

View on GitHub (pinned to 11f27c671c)

Solutions

  1. Regenerate the backup with the current app version so the schema matches PhoneBackup
  2. Validate the JSON (jq . backup.json) and fix syntax/field mismatches or restore from a good copy
  3. Replace expect() with serde_json::from_str::<PhoneBackup>(&data) matched to skip or report invalid backups
  4. Check the backup file was created by this app version and was not truncated during transfer

Example fix

// before
let phone_backup: PhoneBackup =
    serde_json::from_str(&data).expect("Unable to parse backup file");
// after
let phone_backup: PhoneBackup = match serde_json::from_str(&data) {
    Ok(b) => b,
    Err(e) => { error!("Unable to parse backup file `{}`: {}", backup.path().display(), e); return vec![]; }
};
Defensive patterns

Strategy: validation

Validate before calling

// Validate the backup before the library parses it
let data = std::fs::read_to_string(backup.path())?;
let v: serde_json::Value = serde_json::from_str(&data)?;
if v.get("users").and_then(|u| u.as_array()).is_none() {
    return Err("backup file has no valid 'users' array");
}

Type guard

fn is_valid_backup(data: &str) -> bool {
    serde_json::from_str::<serde_json::Value>(data)
        .ok()
        .map(|v| v.get("users").map(|u| u.is_array()).unwrap_or(false))
        .unwrap_or(false)
}

Try / catch

match std::panic::catch_unwind(|| list_available_backup_user(backup.clone())) {
    Ok(users) => users,
    Err(_) => { eprintln!("Unable to parse backup file — regenerate it"); vec![] }
}

Prevention

When it happens

Trigger: Selecting a backup file in update/list flow whose content is truncated, hand-edited, from an older app version with a different PhoneBackup schema, or not JSON at all.

Common situations: Restoring a backup produced by an older UAD version after struct fields were renamed; user edited or moved/corrupted the backup file; picking a non-backup file from the backup directory.

Understand the failure class

Related errors


AI-assisted analysis of 0x192/universal-android-debloater@11f27c671c (2026-09-02). Data as JSON: /api/errors/cade49f1aa77bfbe. Report an issue: GitHub.