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
- Inspect the history/data directory configuration (`MISE_DATA_DIR`, settings) for stray NUL or `\0` sequences and fix the value.
- Re-set the env var from a clean shell: `export MISE_DATA_DIR=$(printf '%s' "$value" | tr -d '\0')`.
- Point the data dir at a simple ASCII path (e.g. `%USERPROFILE%\.local\share\mise`) and retry.
- 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
- Set MISE_DATA_DIR from trusted, text-only sources
- Sanitize interpolated config values with `tr -d '\0'` in scripts
- Re-save config files as clean UTF-8 if they were edited with binary tools
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
- invalid tool path {s:?}: extended-length and device paths (\
- TEMP is too long to replace mise.exe safely ({len} UTF-16 co
- remote task path is not a regular file or directory: {}
- Ruby engine '{}' is not supported on Windows. Only standard
- only available on unix
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/624d94bd309ba8e6.
Report an issue: GitHub.