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

  1. Pass multi-line data through stdin or a temp file instead of argv
  2. Strip or replace \r and \n in arguments whenever the target is a .bat/.cmd
  3. 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

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


AI-assisted analysis of denoland/deno@9ad36f7a2c (2026-08-20). Data as JSON: /api/errors/fb99ede2641f6b6a. Report an issue: GitHub.