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

  1. Open the settings/tree in read-write mode before mutating values.
  2. Create/open the registry key with KEY_SET_VALUE (or recreate it) before writing.
  3. Check existence/writability first: if the key is gone, re-create it via the library's key-opening API.
  4. 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

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


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)