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

Unable to parse

Error message

Unable to parse

What it means

load_debloat_lists deserializes the freshly downloaded remote list text into PackageHashMap with `serde_json::from_str(&text).expect("Unable to parse")`. If the downloaded body is not the expected JSON shape, this panics. The parse is the last trust boundary between the remote HTTP endpoint and the app's package metadata.

Solutions

  1. Replace .expect with error handling that returns OperationResult::Retry / falls back to get_local_lists, as the Err branch already does.
  2. Validate the response content-type or a JSON sanity check (e.g. it starts with '{') before deserializing.
  3. Fix the remote list URL to point at the official raw JSON endpoint.
  4. Log the first bytes of the failing body to diagnose what was actually received.

Example fix

// before
let list: PackageHashMap =
    serde_json::from_str(&text).expect("Unable to parse");
// after
let list: PackageHashMap = serde_json::from_str(&text)
    .unwrap_or_else(|e| {
        warn!("Remote debloat list is invalid JSON: {e}");
        error = true;
        PackageHashMap::new()
    });
Defensive patterns

Strategy: fallback

Validate before calling

let trimmed = text.trim_start();
if !trimmed.starts_with('{') {
    eprintln!("remote list did not return JSON (starts with {:?})", &trimmed[..trimmed.len().min(20)]);
}

Try / catch

match serde_json::from_str::<PackageHashMap>(&text) {
    Ok(list) => OperationResult::Ok(list),
    Err(e) => { warn!("invalid remote list JSON: {e}"); OperationResult::Retry(PackageHashMap::new()) }
}

Prevention

When it happens

Trigger: load_debloat_lists succeeding at the HTTP GET but receiving non-JSON or wrong-schema JSON: the URL returns an HTML error/login page, a truncated body, a CDN 404 page, or the upstream list JSON schema changed incompatibly.

Common situations: Proxies or captive portals replacing the response; the remote list repository moved/renamed and the old URL now serves HTML; a partial/mirror copy of the list with malformed JSON; network cut mid-transfer producing truncated text.

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/503b755b19b1b636. Report an issue: GitHub.

Appendix: source

Thrown at crates/uad-core/src/uad_lists.rs:233

                    /universal-android-debloater\
                    /main\
                    /resources\
                    /assets\
                    /{LIST_FNAME}"
            ))
            .call()
            {
                Ok(mut data) => {
                    // https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/discussions/608
                    let text = data
                        .body_mut()
                        .with_config()
                        .limit(1 << (3 + 10 + 10))
                        .read_to_string()
                        .expect("remote list is bigger than 8MiB");
                    fs::write(cached_uad_lists.clone(), &text).expect("Unable to write file");
                    let list: PackageHashMap =
                        serde_json::from_str(&text).expect("Unable to parse");
                    OperationResult::Ok(list)
                }
                Err(e) => {
                    warn!("Could not load remote debloat list: {e}");
                    error = true;
                    OperationResult::Retry(PackageHashMap::new())
                }
            }
        })
        .unwrap_or_else(|_| get_local_lists())
    } else {
        warn!("Could not load remote debloat list");
        get_local_lists()
    };

    (if error { Err } else { Ok })(list)
}

View on GitHub (pinned to 64465c850c)