ramensoftware/windhawk · error · SettingsError
set on a read-only/absent key
Error message
set on a read-only/absent key
What it means
set_raw was called but the object has no writable key handle — the registry tree was opened read-only or the key does not exist and could not be opened for write. The write cannot proceed, so it is refused with this message instead of attempting a doomed API call.
Solutions
- Open the settings/tree in read-write mode before mutating values.
- Create/open the registry key with KEY_SET_VALUE (or recreate it) before writing.
- Check existence/writability first: if the key is gone, re-create it via the library's key-opening API.
- Avoid mutating settings objects that were constructed for read-only inspection.
Example fix
// before
let s = Settings::open_read_only(path)?;
s.set_int("n", 1)?; // Err: set on a read-only/absent key
// after
let mut s = Settings::open_read_write(path)?; // writable handle
s.set_int("n", 1)?; Defensive patterns
Strategy: validation
Validate before calling
// check writability before mutating
if !settings.is_writable() { return Err("settings opened read-only".into()); } Try / catch
match settings.set_int("n", 1) {
Err(e) if e.message().contains("read-only/absent") => {
let mut w = Settings::open_read_write(path)?;
w.set_int("n", 1)?;
}
r => r?,
} Prevention
- Open settings read-write whenever you plan to mutate.
- Track read-only vs read-write objects in types (separate handles) to catch misuse at compile time.
- Verify the backing key exists before writes; recreate missing keys.
- Do not share read-only views into code paths that save changes.
When it happens
Trigger: Calling set_string/set_int/set_binary (via set_raw) on a settings object opened in read-only mode, or when the backing registry key is absent and no write handle could be acquired.
Common situations: Reading a mod's settings from a viewer/tool that opens keys read-only, then trying to save changes; key removed by an uninstaller while the settings object is open; opening a wrong hive/path that does not exist.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- RegSetValueEx
- value name contains a NUL
- RegQueryValueEx (size)
- RegQueryValueEx (data)
- value is too large for the registry
AI-assisted analysis of ramensoftware/windhawk@61d99ed8e1 (2026-09-12).
Data as JSON: /api/errors/f1173305e1777d30.
Report an issue: GitHub.
Appendix: source
Thrown at src/windhawk-core/windows/src/registry.rs:453
&mut value_type,
buf.as_mut_ptr(),
&mut data_size,
)
};
if rc == ERROR_FILE_NOT_FOUND {
return Ok(None);
}
if rc != ERROR_SUCCESS {
return Err(self.err("get", rc, "RegQueryValueEx (data)"));
}
buf.truncate(data_size as usize);
Ok(Some((value_type, buf)))
}
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).View on GitHub (pinned to 61d99ed8e1)