ramensoftware/windhawk · error · SettingsError
odd-length or non-hex value
Error message
odd-length or non-hex value
What it means
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.
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.
Example fix
// before
let data = settings.get_binary("blob")?; // Err: odd-length or non-hex
// after
let data = match settings.get_binary("blob") {
Ok(d) => d.unwrap_or_default(),
Err(_) => { settings.set_binary("blob", &default_bytes)?; default_bytes.to_vec() }
}; Defensive patterns
Strategy: validation
Validate before calling
fn is_hex(s: &str) -> bool { s.len() % 2 == 0 && s.chars().all(|c| c.is_ascii_hexdigit()) }
// call is_hex(&stored) before get_binary Type guard
fn looks_like_hex_blob(s: &str) -> bool { s.len() % 2 == 0 && s.bytes().all(|b| b.is_ascii_hexdigit()) } Try / catch
let data = settings.get_binary("blob").or_else(|_| {
settings.set_binary("blob", &[])?;
Ok(None)
})?; Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
Related errors
- value name contains a NUL
- GetPrivateProfileString
- value name
- value contains a NUL character
- value contains a line break
AI-assisted analysis of ramensoftware/windhawk@61d99ed8e1 (2026-09-12).
Data as JSON: /api/errors/646d5b5022509372.
Report an issue: GitHub.
Appendix: source
Thrown at src/windhawk-core/windows/src/ini.rs:217
write_profile(&self.file, &self.section, Some(name), Some(&escaped))
}
fn get_int(&self, name: &str) -> Result<Option<i32>, SettingsError> {
Ok(self.get_string(name)?.map(|s| parse_c_int(&s)))
}
fn set_int(&mut self, name: &str, value: i32) -> Result<(), SettingsError> {
// SetInt -> SetString(to_wstring(value)); a decimal triggers no
// escaping, but route through set_string for exactness.
self.set_string(name, &value.to_string())
}
fn get_binary(&self, name: &str) -> Result<Option<Vec<u8>>, SettingsError> {
match self.get_string(name)? {
None => Ok(None),
Some(s) => decode_hex(&s)
.map(Some)
.ok_or_else(|| self.err("get_binary", 0, "odd-length or non-hex value")),
}
}
fn set_binary(&mut self, name: &str, value: &[u8]) -> Result<(), SettingsError> {
self.set_string(name, &encode_hex(value))
}
fn remove(&mut self, name: &str) -> Result<(), SettingsError> {
self.check_name("remove", name)?;
// WritePrivateProfileString(section, name, NULL, file) removes the
// value.
write_profile(&self.file, &self.section, Some(name), None)
}
fn enum_values(&self) -> Result<Vec<(String, TreeValue)>, SettingsError> {
let names = enum_profile_names(&self.file, &self.section)
.map_err(|os| self.err("enum", os, "GetPrivateProfileString"))?;
let mut out = Vec::with_capacity(names.len());View on GitHub (pinned to 61d99ed8e1)