ramensoftware/windhawk · error · SettingsError
value name
Error message
value name {name:?} {why} What it means
IniTree::set_string first validates the value name with unrepresentable_name and refuses names that cannot be faithfully represented in the INI backend (message: "value name {name:?} {why}"). This exists because Win32 INI APIs and the registry backend would otherwise treat such names differently, so the write is rejected up front for consistency.
Solutions
- Check the unrepresentable_name rejection reason in the error and sanitize the name accordingly (strip NUL/invalid characters).
- Validate names before writing: only allow names you would accept on read too.
- Use the registry backend for names that genuinely cannot live in an INI line.
- Surface the error to the caller instead of silently writing a truncated name.
Example fix
// before
settings.set_string(user_supplied_key, "1")?;
// after
let key = user_supplied_key.split('\0').next().unwrap_or_default();
if !key.is_empty() {
settings.set_string(key, "1")?;
} Defensive patterns
Strategy: validation
Validate before calling
fn writable_name(name: &str) -> Result<&str, String> {
if name.contains('\0') { Err(format!("name {name:?} contains NUL")) }
else if name.is_empty() { Err("name is empty".into()) }
else { Ok(name) }
} Type guard
fn writable_name(name: &str) -> Option<&str> {
(!name.contains('\0') && !name.is_empty()).then_some(name)
} Try / catch
if let Err(e) = ini.set_string(&key, &value) {
if e.to_string().contains("value name") {
let key = sanitize(&key);
ini.set_string(&key, &value)?;
} else { return Err(e.into()); }
} Prevention
- Sanitize setting names once at the configuration boundary, not at each call site.
- Restrict generated key names to a safe charset (alphanumeric, dot, dash, underscore).
- Test write/read round-trips with the same names to catch backend mismatches early.
- Do not persist raw interop strings as setting names.
When it happens
Trigger: Calling set_string — also reached via set_int and set_binary — with a name that unrepresentable_name rejects (e.g. a name containing NUL or otherwise unrepresentable in the INI line format).
Common situations: Names built from raw buffers containing terminators; programmatic key generation embedding invalid characters; migrating registry-stored settings with names that only the registry can represent.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- value name contains a NUL
- value contains a NUL character
- GetPrivateProfileString
- value contains a line break
- Initial settings arrays must contain at least one template…
AI-assisted analysis of ramensoftware/windhawk@61d99ed8e1 (2026-09-12).
Data as JSON: /api/errors/635fc0f9722ea64c.
Report an issue: GitHub.
Appendix: source
Thrown at src/windhawk-core/windows/src/ini.rs:179
/// one answer whichever operation carries it, in both storage modes.
fn check_name(&self, op: &'static str, name: &str) -> Result<(), SettingsError> {
if name.contains('\0') {
return Err(self.err(op, 0, &format!("value name {name:?} contains a NUL")));
}
Ok(())
}
}
impl SettingsTree for IniTree {
fn get_string(&self, name: &str) -> Result<Option<String>, SettingsError> {
self.check_name("get", name)?;
get_profile_string(&self.file, &self.section, name)
.map_err(|os| self.err("get", os, "GetPrivateProfileString"))
}
fn set_string(&mut self, name: &str, value: &str) -> Result<(), SettingsError> {
if let Some(why) = unrepresentable_name(name) {
return Err(self.err("set", 0, &format!("value name {name:?} {why}")));
}
// `WritePrivateProfileStringW` takes the value as a NUL-terminated
// string, so an embedded NUL ends it: everything after it is dropped and
// the call still reports success. `escape_ini_value` cannot rescue that
// (the INI line format has no encoding for a NUL), so refuse the write
// rather than store a silently truncated value.
if value.contains('\0') {
return Err(self.err("set", 0, "value contains a NUL character"));
}
// An INI entry ends at the line break, so a value carrying one cannot
// be stored either: it would read back cut at the break, with the rest
// of it parsed as further lines of a file that also holds the mod's
// `[Mod]` config. Refused for the same reason as a NUL - the registry
// backend stores both halves of such a value faithfully, and a write
// that cannot keep the value is better refused than reported as done.
if value.contains(['\r', '\n']) {
return Err(self.err("set", 0, "value contains a line break"));
}View on GitHub (pinned to 61d99ed8e1)