{"record":{"id":"646d5b5022509372","repo":"ramensoftware/windhawk","slug":"odd-length-or-non-hex-value","errorCode":null,"errorMessage":"odd-length or non-hex value","messagePattern":"odd-length or non-hex value","errorType":"validation","errorClass":"SettingsError","httpStatus":null,"severity":"error","filePath":"src/windhawk-core/windows/src/ini.rs","lineNumber":217,"sourceCode":"        write_profile(&self.file, &self.section, Some(name), Some(&escaped))\n    }\n\n    fn get_int(&self, name: &str) -> Result<Option<i32>, SettingsError> {\n        Ok(self.get_string(name)?.map(|s| parse_c_int(&s)))\n    }\n\n    fn set_int(&mut self, name: &str, value: i32) -> Result<(), SettingsError> {\n        // SetInt -> SetString(to_wstring(value)); a decimal triggers no\n        // escaping, but route through set_string for exactness.\n        self.set_string(name, &value.to_string())\n    }\n\n    fn get_binary(&self, name: &str) -> Result<Option<Vec<u8>>, SettingsError> {\n        match self.get_string(name)? {\n            None => Ok(None),\n            Some(s) => decode_hex(&s)\n                .map(Some)\n                .ok_or_else(|| self.err(\"get_binary\", 0, \"odd-length or non-hex value\")),\n        }\n    }\n\n    fn set_binary(&mut self, name: &str, value: &[u8]) -> Result<(), SettingsError> {\n        self.set_string(name, &encode_hex(value))\n    }\n\n    fn remove(&mut self, name: &str) -> Result<(), SettingsError> {\n        self.check_name(\"remove\", name)?;\n        // WritePrivateProfileString(section, name, NULL, file) removes the\n        // value.\n        write_profile(&self.file, &self.section, Some(name), None)\n    }\n\n    fn enum_values(&self) -> Result<Vec<(String, TreeValue)>, SettingsError> {\n        let names = enum_profile_names(&self.file, &self.section)\n            .map_err(|os| self.err(\"enum\", os, \"GetPrivateProfileString\"))?;\n        let mut out = Vec::with_capacity(names.len());","sourceCodeStart":199,"sourceCodeEnd":235,"githubUrl":"https://github.com/ramensoftware/windhawk/blob/61d99ed8e182e1af1b60109612b6763ad1b4b74e/src/windhawk-core/windows/src/ini.rs#L199-L235","documentation":"get_binary decodes the stored string as hex; this error means the stored string was not valid hex (contains non-hex characters) or has an odd number of characters, so it cannot map to whole bytes. It indicates the value was written outside set_binary or corrupted in the INI file.","triggerScenarios":"Calling get_binary on a name whose stored INI value is an arbitrary string (set manually, by an older version, or via set_string) that is not an even-length hex string, e.g. \"abc\" (odd length) or \"xyz\" (non-hex).","commonSituations":"Hand-editing the mod's INI file and mistyping a binary value; a value written by a tool that does not hex-encode; schema migration where a string setting is now read as binary.","solutions":["Check the INI file and fix the value to be an even-length hex string.","Rewrite the value with set_binary so it is stored as proper hex.","Wrap get_binary and treat this error as a missing/corrupt value: fall back to a default and re-store it.","Verify the setting name and section point at the intended key rather than an unrelated string value."],"exampleFix":"// before\nlet data = settings.get_binary(\"blob\")?; // Err: odd-length or non-hex\n// after\nlet data = match settings.get_binary(\"blob\") {\n    Ok(d) => d.unwrap_or_default(),\n    Err(_) => { settings.set_binary(\"blob\", &default_bytes)?; default_bytes.to_vec() }\n};","handlingStrategy":"validation","validationCode":"fn is_hex(s: &str) -> bool { s.len() % 2 == 0 && s.chars().all(|c| c.is_ascii_hexdigit()) }\n// call is_hex(&stored) before get_binary","typeGuard":"fn looks_like_hex_blob(s: &str) -> bool { s.len() % 2 == 0 && s.bytes().all(|b| b.is_ascii_hexdigit()) }","tryCatchPattern":"let data = settings.get_binary(\"blob\").or_else(|_| {\n    settings.set_binary(\"blob\", &[])?;\n    Ok(None)\n})?;","preventionTips":["Only write binary values via set_binary; never via set_string with hand-made text.","Add a checksum/version prefix when the format may evolve.","Guard against hand-edited INI files by validating on load and resetting to defaults.","Keep the encode/decode helpers symmetrical in tests."],"tags":["ini","hex","decoding","corrupt-data"],"backgroundTag":"invalid-argument-format","analyzedSha":"61d99ed8e182e1af1b60109612b6763ad1b4b74e","analyzedAt":"2026-09-12T14:02:41.115Z","contentChangedAt":"2026-09-12T14:02:41.115Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}