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

  1. Check the unrepresentable_name rejection reason in the error and sanitize the name accordingly (strip NUL/invalid characters).
  2. Validate names before writing: only allow names you would accept on read too.
  3. Use the registry backend for names that genuinely cannot live in an INI line.
  4. 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

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


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)