{"record":{"id":"18ea0407d5196113","repo":"Universal-Debloater-Alliance/universal-android-debloater-next-generation","slug":"sdk-version-numeral-must-be-valid","errorCode":null,"errorMessage":"SDK version numeral must be valid","messagePattern":"SDK version numeral must be valid","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/uad-core/src/sync.rs","lineNumber":272,"sourceCode":"    AdbCommand::new()\n        .shell(serial)\n        .getprop(\"ro.product.brand\")\n        // `trim` is just-in-case\n        .map(|s| s.trim().to_string())\n        .unwrap_or_default()\n}\n\n/// Get Android SDK version by querying the\n// `ro.build.version.sdk` property or defaulting to 0.\n///\n/// If `device_serial` is empty, it lets ADB choose the default device.\n#[must_use]\npub fn get_android_sdk(device_serial: &str) -> u8 {\n    AdbCommand::new()\n        .shell(device_serial)\n        .getprop(\"ro.build.version.sdk\")\n        .map_or(0, |sdk| {\n            sdk.parse().expect(\"SDK version numeral must be valid\")\n        })\n}\n\n/// Capture the current state of a package across all non-protected users.\n/// This is used to detect cross-user behavior by comparing before and after states.\n///\n/// Only includes users where the package exists (Some state). Users where the package\n/// doesn't exist (None) are not tracked.\n#[must_use]\npub fn capture_cross_user_states(\n    package_name: &str,\n    device_serial: &str,\n    target_user_id: u16,\n    phone: &Phone,\n) -> Vec<(u16, PackageState)> {\n    phone\n        .user_list\n        .iter()","sourceCodeStart":254,"sourceCodeEnd":290,"githubUrl":"https://github.com/Universal-Debloater-Alliance/universal-android-debloater-next-generation/blob/64465c850c7ed36329e67165ac08501abffb218e/crates/uad-core/src/sync.rs#L254-L290","documentation":"get_android_sdk shells out to `adb shell getprop ro.build.version.sdk` and parses the returned string into a u8. If adb returns anything that is not a valid u8 (empty output, an error string, or a number larger than u8), the `.unwrap_or`-style map_or only covers the command failing — a successful command with unparseable output hits `.expect(\"SDK version numeral must be valid\")` and panics. The library assumes getprop always yields a plain numeric SDK level when the command succeeds.","triggerScenarios":"Calling get_android_sdk(device_serial) when the device's `ro.build.version.sdk` property is non-numeric or exceeds 255: e.g. a device booting into a broken state, a future Android release with SDK > 255, or a device whose getprop output includes extra whitespace/suffix text.","commonSituations":"Connecting to emulators/custom ROMs with malformed build props; testing against a hypothetical Android version whose API level exceeds 255 (u8 overflow); devices in a half-booted or recovery-adjacent state where getprop succeeds but returns garbage.","solutions":["Replace .expect with graceful fallback: `sdk.parse().unwrap_or(0)` so an invalid value is treated the same as a failed adb command.","Change the return type to Result<u8, String> and propagate the parse error to the caller instead of panicking.","Change the return type to u32 (or parse as u32 then clamp) to remove the u8 overflow failure mode.","Log the raw getprop output when parsing fails so the malformed device state is diagnosable."],"exampleFix":"// before\n.map_or(0, |sdk| {\n    sdk.parse().expect(\"SDK version numeral must be valid\")\n})\n// after\n.map_or(0, |sdk| sdk.trim().parse().unwrap_or(0))","handlingStrategy":"fallback","validationCode":"let out = AdbCommand::new().shell(serial).getprop(\"ro.build.version.sdk\").unwrap_or_default();\nlet sdk: Option<u8> = out.trim().parse().ok();\nif sdk.is_none() { eprintln!(\"device {serial} returned non-numeric SDK: {out:?}\"); }","typeGuard":"fn is_valid_sdk(s: &str) -> bool { s.trim().parse::<u8>().is_ok() }","tryCatchPattern":"match sdk_str.trim().parse::<u8>() {\n    Ok(v) => v,\n    Err(e) => { warn!(\"bad SDK value {sdk_str:?}: {e}\"); 0 }\n}","preventionTips":["Never .expect on parse results from external processes; always fall back to a sentinel like 0.","Trim adb/getprop output before parsing (it can carry trailing whitespace or CR on Windows).","Use u32 for SDK levels to survive future Android API growth past 255."],"tags":["panic","parse-error","android","adb"],"backgroundTag":"invalid-argument-format","analyzedSha":"64465c850c7ed36329e67165ac08501abffb218e","analyzedAt":"2026-09-12T09:09:23.137Z","contentChangedAt":"2026-09-12T09:09:23.137Z","schemaVersion":2},"datasetVersion":"2026-09-16T04:17:20.429Z"}