vercel-labs/agent-browser · error · io::Error (InvalidInput)
NUL in Chrome argument
Error message
NUL in Chrome argument
What it means
The `wide` helper converts an OsStr (a Chrome argument such as an exe path, flag, or URL) to a NUL-terminated UTF-16 buffer for Windows APIs like CreateProcessW. Windows native string APIs cannot represent embedded NUL characters, so before appending the terminating NUL the function scans the encoded value for interior 0u16 code units and refuses the input with this error instead of silently truncating the argument. It is a fail-fast input validation guard for Windows process spawning.
Source
Thrown at cli/src/native/cdp/windows_process.rs:301
// SAFETY: Duplicate into our process; ownership is transferred below.
check(unsafe {
DuplicateHandle(
GetCurrentProcess(),
handle,
GetCurrentProcess(),
&mut duplicate,
0,
1,
DUPLICATE_SAME_ACCESS,
)
})?;
owned(duplicate)
}
fn wide(value: &OsStr) -> io::Result<Vec<u16>> {
let mut value: Vec<_> = value.encode_wide().collect();
if value.contains(&0) {
return Err(io::Error::new(
io::ErrorKind::InvalidInput,
"NUL in Chrome argument",
));
}
value.push(0);
Ok(value)
}
/// Quote a single argument for Chrome's Windows C runtime. Backslashes are
/// doubled only before a quote or the closing quote, preserving paths/JSON.
fn quoted(value: &OsStr) -> io::Result<Vec<u16>> {
let value = wide(value)?;
let mut output = vec![b'"' as u16];
let mut slashes = 0;
for &ch in &value[..value.len() - 1] {
if ch == b'\\' as u16 {
slashes += 1;
continue;View on GitHub (pinned to 921a57b64d)
Solutions
- Trim or strip NUL bytes from the argument before passing it to the daemon/CLI, e.g. `s.trim_end_matches('\0')` or filter out 0 bytes from the source data.
- Validate every Chrome argument (path, flags, URL) for '\0' before invoking the library and reject or sanitize at your config-loading boundary.
- If the value comes from reading a file or byte buffer, decode it as UTF-8/UTF-16 properly instead of copying raw bytes that may include terminators.
Example fix
// before
let exe = std::fs::read_to_string("chrome_path.txt")?; // file saved as UTF-16, contains '\0'
daemon.launch(&exe, &["--headless"])?;
// after
let exe = std::fs::read_to_string("chrome_path.txt")?.trim_end_matches('\0').to_string();
assert!(!exe.contains('\0'), "chrome path must not contain NUL bytes");
daemon.launch(&exe, &["--headless"])?; Defensive patterns
Strategy: validation
Validate before calling
fn assert_no_nul(arg: &std::ffi::OsStr) -> std::io::Result<()> {
use std::os::windows::ffi::OsStrExt;
if arg.encode_wide().any(|c| c == 0) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"NUL in Chrome argument",
));
}
Ok(())
}
// run for every arg before launching Chrome:
// assert_no_nul(exe.as_ref())?; for flag in flags { assert_no_nul(flag.as_ref())?; } Type guard
fn is_nul_free(arg: &std::ffi::OsStr) -> bool {
use std::os::windows::ffi::OsStrExt;
!arg.encode_wide().any(|c| c == 0)
} Prevention
- Strip trailing and interior NUL bytes when loading paths or arguments from files, registries, or byte buffers.
- Never build CLI arguments by concatenating raw byte buffers; always go through String/OsStr string APIs.
- Sanitize untrusted/user-supplied input before it becomes a Chrome flag or executable path.
- On Windows, remember OsStr::encode_wide output must not include terminators; the terminating NUL is appended by the spawn helper.
When it happens
Trigger: Calling spawn/quoted paths that pass an OsStr containing interior NUL bytes to `wide(...)`; i.e. any Chrome argument, executable path, working directory, or environment value built from data that contains a 0 byte (e.g. bytes from a file, network input, or a String constructed with '\0' inside).
Common situations: Reading a Chrome path or argument list from a config file, database, or binary source that includes a trailing/interior NUL byte; interpolating untrusted user input into a Chrome flag; reconstructing a path from Windows wide-char data and accidentally keeping the terminator; concatenating byte buffers instead of strings.
Related errors
- The "${action}" action requires a value.
- The "${property}" property requires a selector.
- The "attr" property requires an attribute name.
- The "goto" action requires a url.
- Provide a direction to scroll, or a selector to scroll into
AI-assisted analysis of vercel-labs/agent-browser@921a57b64d (2026-09-10).
Data as JSON: /api/errors/966b9191b5e6c803.
Report an issue: GitHub.