denoland/deno · error

Windows file names may not contain `"` or end with `\`

Error message

Windows file names may not contain `"` or end with `\`

What it means

Batch files (.bat/.cmd) are launched by wrapping the entire command line in an extra quoted cmd.exe /c "..." sequence. A script path containing a double quote would break out of that quoting, and one ending with a backslash would escape the closing quote, so both are rejected with InvalidInput before the command line is assembled.

Source

Thrown at runtime/subprocess_windows/src/process.rs:1487

// Copyright The Rust Project Contributors - MIT
fn make_bat_command_line(
  script: &[u16],
  args: &[&OsStr],
  force_quotes: bool,
) -> io::Result<Vec<u16>> {
  // Set the start of the command line to `cmd.exe /c "`
  // It is necessary to surround the command in an extra pair of quotes,
  // hence the trailing quote here. It will be closed after all arguments
  // have been added.
  // Using /e:ON enables "command extensions" which is essential for the `%` hack to work.
  let mut cmd: Vec<u16> = "/e:ON /v:OFF /d /c \"".encode_utf16().collect();

  // Push the script name surrounded by its quote pair.
  cmd.push(b'"' as u16);
  // Windows file names cannot contain a `"` character or end with `\\`.
  // If the script name does then return an error.
  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,

View on GitHub (pinned to 9ad36f7a2c)

Solutions

  1. Build paths with a join helper instead of concatenation so no trailing backslash remains
  2. Reject or strip double quotes in batch script paths before spawning
  3. Rename the offending file - Windows itself forbids quotes in filenames, so such a path almost always indicates an upstream bug

Example fix

// before - path ends with a backslash before quoting is applied
const c = new Deno.Command(`C:\\tools\\${name}.cmd\\`);

// after
import { join } from "jsr:@std/path";
const c = new Deno.Command(join("C:/tools", `${name}.cmd`));
Defensive patterns

Strategy: validation

Validate before calling

const isSafeWindowsScriptPath = (p: string) =>
  !p.includes('"') && !p.endsWith("\\");
if (/\.(bat|cmd)$/i.test(prog) && !isSafeWindowsScriptPath(prog)) {
  throw new Error(`unsafe batch script path: ${prog}`);
}

Type guard

const isSafeScriptPath = (p: string): p is string =>
  !p.includes('"') && !p.endsWith("\\");

Prevention

When it happens

Trigger: Spawning a .bat/.cmd file whose resolved path contains a " character or ends with a backslash - e.g. new Deno.Command("C:\\tools\\setup.cmd\\") or a path built from unvalidated user input that includes a quote.

Common situations: Paths assembled by string concatenation that keep a trailing separator; filenames containing quotes (extracted from archives or user input); config-driven script paths that were never validated before spawn.

Related errors


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