xai-org/grok-build · error · io::Error
path contains NUL
Error message
path contains NUL
What it means
windows_extended_path converts a path to a Windows extended-length (\\?\ or \\?\UNC\) wide-string path for Win32 APIs that take UTF-16 paths. Windows cannot accept embedded NUL characters in path strings, so if the encoded wide string contains a 0 code unit the function refuses to build the extended path and throws io::ErrorKind::InvalidInput with 'path contains NUL'.
Source
Thrown at crates/codegen/xai-grok-tools/src/persistence.rs:380
PCWSTR(from.as_ptr()),
PCWSTR(to.as_ptr()),
Self::WINDOWS_MOVE_FLAGS,
)
}
.map_err(io::Error::other)
}
#[cfg(windows)]
const WINDOWS_MOVE_FLAGS: windows::Win32::Storage::FileSystem::MOVE_FILE_FLAGS =
windows::Win32::Storage::FileSystem::MOVE_FILE_FLAGS(1 | 8);
#[cfg(windows)]
fn windows_extended_path(path: &Path) -> io::Result<Vec<u16>> {
use std::os::windows::ffi::OsStrExt;
let path = std::path::absolute(path)?;
let mut wide = path.as_os_str().encode_wide().collect::<Vec<_>>();
if wide.contains(&0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"path contains NUL",
));
}
let unc = wide.starts_with(&[92, 92]);
let mut result = if unc { r"\\?\UNC\" } else { r"\\?\" }
.encode_utf16()
.collect::<Vec<_>>();
if unc {
wide.drain(..2);
}
result.extend(wide);
result.push(0);
Ok(result)
}
}
// Old `PersistenceLayer` / `PersistenceRunner` deleted.View on GitHub (pinned to bc7f02eddd)
Solutions
- Trim or strip NUL bytes from the path string before constructing the PathBuf (e.g. split at the first '\0').
- Validate the path with path.as_os_str().to_string_lossy().contains('\0') before calling the API and sanitize it.
- Log the offending path bytes to find where the NUL is being introduced upstream.
Example fix
// before
let path = PathBuf::from(c_string_buf); // may contain '\0'
// after
let cleaned: &str = c_string_buf.split('\0').next().unwrap();
let path = PathBuf::from(cleaned); Defensive patterns
Strategy: validation
Validate before calling
fn path_has_nul(p: &std::path::Path) -> bool {
#[cfg(windows)]
{ use std::os::windows::ffi::OsStrExt; p.as_os_str().encode_wide().any(|c| c == 0) }
#[cfg(not(windows))]
{ false }
}
// if path_has_nul(&p) { sanitize before calling } Type guard
fn is_safe_path(p: &std::path::Path) -> bool {
p.as_os_str().to_str().map(|s| !s.contains('\0')).unwrap_or(false)
} Try / catch
match persistence::save(&path) {
Ok(v) => v,
Err(e) if e.kind() == std::io::ErrorKind::InvalidInput && e.to_string().contains("NUL") => {
let clean = sanitize_nul(&path);
persistence::save(&clean)?
}
Err(e) => return Err(e.into()),
} Prevention
- Never build PathBuf from raw C/F buffers without trimming at the first NUL
- Sanitize paths read from binary configs or network payloads
- Validate paths contain no '\0' at the boundary where they enter your code
When it happens
Trigger: Calling any persistence API on Windows whose path argument (after std::path::absolute) encodes to a wide string containing a NUL code unit — practically only when a Rust OsStr was constructed from raw bytes containing interior NULs, or a path was built from a buffer/string that includes '\0'.
Common situations: Paths read from binary data, fixed-size C buffers, or legacy configs that include a trailing or interior NUL byte; converting C FFI strings to PathBuf without trimming the terminator.
Related errors
- destination has no filename: {}
- cannot rename locked executable {}: {e} Close all running gr
- workflow path has no parent
- workflow path is not UTF-8
- trust store path has no parent
AI-assisted analysis of xai-org/grok-build@bc7f02eddd (2026-08-31).
Data as JSON: /api/errors/a82368f7341a0c05.
Report an issue: GitHub.