Universal-Debloater-Alliance/universal-android-debloater-next-generation · error

Unable to parse backup file

Error message

Unable to parse backup file

What it means

list_available_backup_user reads a backup file and deserializes it into PhoneBackup with serde_json; malformed JSON panics with "Unable to parse backup file". The library assumes any selected .backup/.json file is a valid UAD phone backup.

Solutions

  1. Validate the file with a JSON linter or `jq . file` to confirm it parses.
  2. Confirm the backup was exported by a compatible UAD version (PhoneBackup schema).
  3. Re-export the backup from the original device.
  4. Wrap parsing with serde_json::from_str(...).ok()/Result handling and show a user-facing error instead of panicking.

Example fix

// before
serde_json::from_str::<PhoneBackup>(&data).expect("Unable to parse backup file")
// after
let phone_backup: PhoneBackup = serde_json::from_str(&data)
    .map_err(|e| error!("Unable to parse backup file: {e}")).unwrap_or_default();
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_uad_backup(path: &std::path::Path) -> bool {
    std::fs::read_to_string(path)
        .ok()
        .and_then(|s| serde_json::from_str::<serde_json::Value>(&s).ok())
        .map(|v| v.get("users").is_some())
        .unwrap_or(false)
}

Type guard

fn is_phone_backup(v: &serde_json::Value) -> bool {
    v.get("users").and_then(|u| u.as_array()).map(|a| a.iter().all(|x| x.get("id").is_some())).unwrap_or(false)
}

Try / catch

match std::fs::read_to_string(backup.path) {
    Ok(data) => match serde_json::from_str::<PhoneBackup>(&data) {
        Ok(pb) => { /* use pb.users */ }
        Err(e) => eprintln!("Unable to parse backup file: {e}"),
    },
    Err(e) => eprintln!("read failed: {e}"),
}

Prevention

When it happens

Trigger: Calling list_available_backup_user(backup) with a file that is not valid JSON, is an older/incompatible backup schema, is empty, or has a different encoding (UTF-16/BOM).

Common situations: User selects a random or renamed file in the restore dialog; backups exported by an older UAD version with a changed PhoneBackup schema; truncated downloads.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


AI-assisted analysis of Universal-Debloater-Alliance/universal-android-debloater-next-generation@64465c850c (2026-09-12). Data as JSON: /api/errors/5c8a8b6cc88fbab3. Report an issue: GitHub.

Appendix: source

Thrown at crates/uad-core/src/save.rs:82

        Err(err) => Err(err.to_string()),
    }
}

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

#[must_use]
pub fn list_available_backup_user(backup: DisplayablePath) -> Vec<User> {
    match fs::read_to_string(backup.path) {
        Ok(data) => serde_json::from_str::<PhoneBackup>(&data)
            .expect("Unable to parse backup file")
            .users
            .into_iter()
            .map(|u| User {
                id: u.id,
                index: 0,
                protected: false,
            })
            .collect(),
        Err(e) => {
            error!("[BACKUP]: Selected backup file not found: {e}");
            vec![]
        }
    }
}

#[derive(Debug)]
pub struct BackupPackage {
    pub i_user: usize,

View on GitHub (pinned to 64465c850c)