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
- Replace .expect with error handling that returns OperationResult::Retry / falls back to get_local_lists, as the Err branch already does.
- Validate the response content-type or a JSON sanity check (e.g. it starts with '{') before deserializing.
- Fix the remote list URL to point at the official raw JSON endpoint.
- 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
- Never deserialize an HTTP body without a fallback to the cached/bundled list.
- Sanity-check the response body shape (first char, content-type) before parsing.
- Beware proxies/captive portals replacing JSON responses with HTML.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Unable to parse backup file
- SDK version numeral must be valid
- remote list is bigger than 8MiB
- {e}
- There must be 1 tab after serial
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)