denoland/deno · error
batch file arguments are invalid
Error message
batch file arguments are invalid
What it means
Arguments to a .bat/.cmd child are appended into a single cmd.exe command line after the script name. A carriage return or newline inside an argument would terminate the current command and let the remainder execute as a new cmd.exe command, so any batch-file argument containing \r or \n is rejected with InvalidInput to prevent argument/command injection.
Source
Thrown at runtime/subprocess_windows/src/process.rs:1504
if script.contains(&(b'"' as u16)) || script.last() == Some(&(b'\\' as u16)) {
return Err(std::io::Error::new(
io::ErrorKind::InvalidInput,
"Windows file names may not contain `\"` or end with `\\`",
));
}
cmd.extend_from_slice(script.strip_suffix(&[0]).unwrap_or(script));
cmd.push(b'"' as u16);
// Append the arguments.
// FIXME: This needs tests to ensure that the arguments are properly
// reconstructed by the batch script by default.
for arg in args.iter().skip(1) {
cmd.push(' ' as u16);
let arg_bytes = arg.as_encoded_bytes();
// Disallow \r and \n as they may truncate the arguments.
const DISALLOWED: &[u8] = b"\r\n";
if arg_bytes.iter().any(|c| DISALLOWED.contains(c)) {
return Err(std::io::Error::new(
io::ErrorKind::InvalidInput,
r#"batch file arguments are invalid"#,
));
}
append_bat_arg(&mut cmd, arg, force_quotes)?;
}
// Close the quote we left opened earlier.
cmd.push(b'"' as u16);
Ok(cmd)
}
// lifted from https://github.com/rust-lang/rust/blob/bc1d7273dfbc6f8a11c0086fa35f6748a13e8d3c/library/std/src/sys/args/windows.rs#L220C1-L291C2
// Copyright The Rust Project Contributors - MIT
fn append_bat_arg(
cmd: &mut Vec<u16>,
arg: &OsStr,View on GitHub (pinned to 9ad36f7a2c)
Solutions
- Pass multi-line data through stdin or a temp file instead of argv
- Strip or replace \r and \n in arguments whenever the target is a .bat/.cmd
- Invoke the underlying .exe directly (node.exe instead of npm.cmd) so batch quoting rules do not apply
Example fix
// before
const c = new Deno.Command("wrap.cmd", { args: [commitMsg] }); // commitMsg contains \n
// after - send multi-line data via stdin
const c = new Deno.Command("wrap.cmd", { stdin: "piped" });
const child = c.spawn();
const w = child.stdin.getWriter();
await w.write(new TextEncoder().encode(commitMsg));
await w.close(); Defensive patterns
Strategy: validation
Validate before calling
const isBatch = (f: string) => /\.(bat|cmd)$/i.test(f);
if (isBatch(prog) && args.some((a) => /[\r\n]/.test(a))) {
throw new Error("multi-line arguments cannot be passed to batch files; use stdin");
} Type guard
const isSingleLine = (s: string): s is string => !/[\r\n]/.test(s);
Prevention
- Treat any argument bound for a .bat/.cmd as untrusted: reject CR/LF
- Default to stdin or temp files for multi-line payloads
- Call the underlying .exe directly to sidestep batch quoting rules
When it happens
Trigger: new Deno.Command("script.bat", { args: [multilineText] }) - or spawning an npm/npx .cmd wrapper on Windows - where any argument after the script name contains a carriage return or line feed, such as commit messages, prompt bodies, or file contents passed as argv.
Common situations: Forwarding user-typed multi-line text (git commit -m, AI prompts) to a .cmd wrapper; CI scripts passing heredoc-like strings; data read from CRLF files and passed verbatim as arguments on Windows.
Related errors
- Windows file names may not contain `"` or end with `\`
- {}: ({}) {}
- failed to unregister: {}
- nul byte found in provided data
- Process not found
AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20).
Data as JSON: /api/errors/fb99ede2641f6b6a.
Report an issue: GitHub.