ramensoftware/windhawk · error · SettingsError

RegSetValueEx

Error message

RegSetValueEx

What it means

The RegSetValueExW call in set_raw failed with an OS error other than ERROR_SUCCESS; the write to the registry value did not happen. The error message carries the Win32 function name plus the underlying OS error code.

Solutions

  1. Check the wrapped OS code — ERROR_ACCESS_DENIED means elevate privileges or choose a writable location (e.g. HKCU instead of HKLM).
  2. Re-open the key with KEY_SET_VALUE if the handle may have gone stale.
  3. Verify the key still exists and was not removed concurrently; recreate if needed.
  4. Reduce value size / use an allowed type if the API rejected type or quota.

Example fix

// before
settings.set_string("key", "v")?; // fails under HKLM without admin
// after
// open settings under HKCU (writable) or handle elevation
match settings.set_string("key", "v") {
    Err(e) if e.os_code() == ERROR_ACCESS_DENIED => eprintln!("run elevated or use HKCU"),
    r => r?,
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure the key exists and the process has write access (e.g. prefer HKCU over HKLM)

Try / catch

match settings.set_string("k", "v") {
    Err(e) if e.message().starts_with("RegSetValueEx") => {
        if e.message().contains("access is denied") { /* elevate or switch to HKCU */ }
    }
    r => r?,
}

Prevention

When it happens

Trigger: Writing a value when access is denied (ERROR_ACCESS_DENIED), the key handle is invalid, the value type/length combination is rejected, registry quota is exceeded, or the key was deleted concurrently.

Common situations: Writing to HKLM without admin rights (UAC); anti-malware or Group Policy locking mod keys; key deleted by another process mid-run; quota limits on very large values.

Related errors


AI-assisted analysis of ramensoftware/windhawk@61d99ed8e1 (2026-09-12). Data as JSON: /api/errors/8d953acb3f695d79. Report an issue: GitHub.

Appendix: source

Thrown at src/windhawk-core/windows/src/registry.rs:466

    }

    fn set_raw(&self, name: &str, value_type: u32, data: &[u8]) -> Result<(), SettingsError> {
        self.check_name("set", name)?;
        let Some(hkey) = self.hkey() else {
            return Err(self.err("set", 0, "set on a read-only/absent key"));
        };
        let name_w = to_wide(name);
        // A length the API's u32 cannot hold has no honest value to pass, and a
        // clamped one would hand the call a length the buffer does not have.
        let len = u32::try_from(data.len())
            .map_err(|_| self.err("set", 0, "value is too large for the registry"))?;
        // SAFETY: name_w is NUL-terminated; data/len describe a valid buffer.
        let rc =
            unsafe { RegSetValueExW(hkey, name_w.as_ptr(), 0, value_type, data.as_ptr(), len) };
        if rc == ERROR_SUCCESS {
            Ok(())
        } else {
            Err(self.err("set", rc, "RegSetValueEx"))
        }
    }
}

/// Decode a `REG_SZ` byte buffer (UTF-16LE, possibly NUL-terminated).
fn decode_sz(bytes: &[u8]) -> String {
    let units: Vec<u16> = bytes
        .chunks_exact(2)
        .map(|c| u16::from_le_bytes([c[0], c[1]]))
        .collect();
    from_wide_nul(&units)
}

fn decode_dword(bytes: &[u8]) -> Option<i32> {
    if bytes.len() == 4 {
        Some(i32::from_le_bytes([bytes[0], bytes[1], bytes[2], bytes[3]]))
    } else {
        None

View on GitHub (pinned to 61d99ed8e1)