jdx/mise · error

history directory contains a NUL character

Error message

history directory contains a NUL character

What it means

On Windows, `create_private_dir` encodes the history directory path as UTF-16 for a Win32 API call. Interior NUL characters cannot appear in a valid Win32 path string, so if the encoded wide path contains a NUL, the call is refused up front with this error. This is a defensive check that a sanitized path never silently truncates at the Win32 boundary.

Source

Thrown at src/system/history/store.rs:135

    let sddl: Vec<u16> = "D:P(A;OICI;FA;;;OW)\0".encode_utf16().collect();
    let mut descriptor = std::ptr::null_mut();
    // SAFETY: both pointers are valid for the call; Windows allocates the
    // descriptor, released with LocalFree below on every path.
    if unsafe {
        ConvertStringSecurityDescriptorToSecurityDescriptorW(
            sddl.as_ptr(),
            1,
            &mut descriptor,
            std::ptr::null_mut(),
        )
    } == 0
    {
        return Err(std::io::Error::last_os_error().into());
    }
    let result = (|| -> Result<()> {
        let mut path: Vec<u16> = dir.as_os_str().encode_wide().collect();
        if path.contains(&0) {
            eyre::bail!("history directory contains a NUL character");
        }
        path.push(0);
        let attributes = SECURITY_ATTRIBUTES {
            nLength: std::mem::size_of::<SECURITY_ATTRIBUTES>() as u32,
            lpSecurityDescriptor: descriptor,
            bInheritHandle: 0,
        };
        if !dir.is_dir()
            // SAFETY: path is NUL-terminated and attributes/descriptor remain live.
            && unsafe { CreateDirectoryW(path.as_ptr(), &attributes) } == 0
            && !dir.is_dir()
        {
            return Err(std::io::Error::last_os_error().into());
        }
        // Also tighten an existing directory instead of trusting inherited ACLs.
        // SAFETY: path and descriptor are valid until the call returns.
        if unsafe {
            SetFileSecurityW(

View on GitHub (pinned to afd2eddd3a)

Solutions

  1. Inspect the history/data directory configuration (`MISE_DATA_DIR`, settings) for stray NUL or `\0` sequences and fix the value.
  2. Re-set the env var from a clean shell: `export MISE_DATA_DIR=$(printf '%s' "$value" | tr -d '\0')`.
  3. Point the data dir at a simple ASCII path (e.g. `%USERPROFILE%\.local\share\mise`) and retry.
  4. If the value comes from a config file, re-save that file as clean UTF-8 without embedded NULs.

Example fix

// before: path built from raw bytes
let dir = PathBuf::from(OsString::from_vec(bytes));
// after: strip NULs before use
let dir = PathBuf::from(String::from_utf8_lossy(&bytes).replace('\0', ""));
Defensive patterns

Strategy: validation

Validate before calling

let dir = std::env::var("MISE_DATA_DIR").unwrap_or_default();
if dir.contains('\0') {
    eprintln!("MISE_DATA_DIR contains a NUL character; fix before running");
}

Type guard

fn is_valid_dir_name(p: &std::path::Path) -> bool {
    p.as_os_str().to_str().map(|s| !s.contains('\0')).unwrap_or(false)
}

Try / catch

match init_history_store() {
    Ok(store) => {},
    Err(e) if e.to_string().contains("NUL character") => {
        eprintln!("data-dir path is corrupted; reset MISE_DATA_DIR");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Constructing a history directory path that contains an embedded NUL character — typically from a corrupted environment variable, a bad config value, or a string built from binary data — then attempting to create the private directory on Windows.

Common situations: MISE_DATA_DIR or similar env var set from a mis-decoded value; a config file with a literal `\0` escape expanded into the path; a script interpolating binary output into the data-dir setting.

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 jdx/mise@afd2eddd3a (2026-09-09). Data as JSON: /api/errors/624d94bd309ba8e6. Report an issue: GitHub.