denoland/deno · error
env contains NUL
Error message
env contains NUL
What it means
While building the envp array for posix_spawn, `flatten()` joins each inherited or overridden environment key and value into a single `KEY=value` C string. If either side contains a NUL byte, `CString::new` fails and the whole spawn is rejected with `io::ErrorKind::InvalidInput` ("env contains NUL").
Source
Thrown at cli/tools/desktop.rs:5764
match v {
Some(v) => {
env_map.insert(k.to_os_string(), v.to_os_string());
}
None => {
env_map.remove(k);
}
}
}
let envp: Vec<CString> = env_map
.into_iter()
.map(|(k, v)| {
let mut s =
Vec::with_capacity(k.as_bytes().len() + 1 + v.as_bytes().len());
s.extend_from_slice(k.as_bytes());
s.push(b'=');
s.extend_from_slice(v.as_bytes());
CString::new(s).map_err(|_| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
"env contains NUL",
)
})
})
.collect::<std::io::Result<_>>()?;
let cwd = match cmd.get_current_dir() {
Some(p) => {
Some(CString::new(p.as_os_str().as_bytes()).map_err(|_| {
std::io::Error::new(std::io::ErrorKind::InvalidInput, "cwd has NUL")
})?)
}
None => None,
};
Ok((program, argv, envp, cwd))
}
pub fn spawn(cmd: &std::process::Command) -> std::io::Result<Child> {View on GitHub (pinned to f7822238ca)
Solutions
- Validate both key and value with a NUL check in `Command::env` calls and when inheriting unusual env.
- Fix the producer writing 0x00 into the environ entry; encode binary values (hex/base64) instead of raw bytes.
- Clear or replace the offending variable before spawning if it is not needed by the child.
Example fix
// before
for (k, v) in &extra_env { cmd.env(k, v); }
// after
fn reject_nul(bytes: &[u8], what: &str) -> std::io::Result<()> {
if bytes.contains(&0) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("{what} contains NUL"),
));
}
Ok(())
}
for (k, v) in &extra_env {
reject_nul(k.as_bytes(), "env key")?;
reject_nul(v.as_bytes(), "env value")?;
cmd.env(k, v);
} Defensive patterns
Strategy: validation
Validate before calling
fn reject_nul(bytes: &[u8], what: &str) -> std::io::Result<()> {
if bytes.contains(&0) {
return Err(std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("{what} contains NUL"),
));
}
Ok(())
}
for (k, v) in cmd.get_envs() {
reject_nul(k.as_bytes(), "env key")?;
if let Some(v) = v { reject_nul(v.as_bytes(), "env value")?; }
} Type guard
fn is_nul_free(s: &std::ffi::OsStr) -> bool {
!s.as_bytes().contains(&0)
} Try / catch
match spawn(cmd) {
Err(e) if e.kind() == std::io::ErrorKind::InvalidInput => {
// scan env for the offending entry and drop/fix it before retrying
}
other => other,
} Prevention
- Never pass raw binary secrets via env vars — encode them
- Validate inherited environ when the parent environment is machine-generated
- Drop unneeded variables with env_remove instead of forwarding everything
When it happens
Trigger: `Command::env(k, v)` (or the inherited process environment) carrying a key or value with an embedded 0x00 — e.g. tokens decoded from binary formats, base64-mangled secrets, or `env::vars_os` from a parent process with malformed environ entries.
Common situations: Passing machine-generated credentials/tokens as env vars; containers whose environ was written by a buggy injector; debug builds that stuff packed structs into env values.
Related errors
AI-assisted analysis of denoland/deno@f7822238ca (2026-08-20).
Data as JSON: /api/errors/afa95fabf02a822c.
Report an issue: GitHub.