rust-lang/cargo · error · io::Error

argument for argfile contains newlines

Error message

argument for argfile contains newlines: `{arg}`

What it means

In the same argfile-building loop, after confirming each argument is valid UTF-8, Cargo checks `arg.contains('\n')`. Because the argfile format writes exactly one argument per line (`writeln!(buf, "{arg}")`), an embedded newline would split a single argument across lines and corrupt parsing. Any argument containing a newline triggers this `io::Error` (kind `Other`).

Solutions

  1. Strip or replace newlines in the argument before passing it to the `ProcessBuilder`.
  2. Write multiline content to a separate file and pass the file path as the argument instead.
  3. Encode the argument (e.g. base64 or JSON on a single line) so it contains no newlines.

Example fix

// before
let cfg = "key1=val1\nkey2=val2";
pb.arg(cfg);

// after
let cfg = "key1=val1\nkey2=val2".replace('\n', " ");
pb.arg(cfg);
Defensive patterns

Strategy: validation

Validate before calling

fn args_no_newlines(args: &[String]) -> Result<(), String> {
    for a in args {
        if a.contains('\n') { return Err(format!("arg contains newline: {a:?}")); }
    }
    Ok(())
}
// call before populating a ProcessBuilder that may use argfile mode

Prevention

When it happens

Trigger: A `ProcessBuilder` argument containing a `\n` byte, materialized into an argfile. For example, a multiline string value, a JSON/document argument with newlines, or a path with an embedded newline (possible on some filesystems).

Common situations: Passing a multiline configuration snippet, script body, or structured payload as a single command-line argument; tooling that embeds raw file contents into an argument; unusual filenames containing newlines.

Related errors


AI-assisted analysis of rust-lang/cargo@42eee92bc9 (2026-08-11). Data as JSON: /api/errors/05a14b8be4489cc3. Report an issue: GitHub.

Appendix: source

Thrown at crates/cargo-util/src/process_builder.rs:513

        arg.push(tmp.path());
        let mut cmd = self.build_command_without_args();
        cmd.arg(arg);
        tracing::debug!("created argfile at {} for {self}", tmp.path().display());

        let cap = self.get_args().map(|arg| arg.len() + 1).sum::<usize>();
        let mut buf = Vec::with_capacity(cap);
        for arg in &self.args {
            let arg = arg.to_str().ok_or_else(|| {
                io::Error::new(
                    io::ErrorKind::Other,
                    format!(
                        "argument for argfile contains invalid UTF-8 characters: `{}`",
                        arg.to_string_lossy()
                    ),
                )
            })?;
            if arg.contains('\n') {
                return Err(io::Error::new(
                    io::ErrorKind::Other,
                    format!("argument for argfile contains newlines: `{arg}`"),
                ));
            }
            writeln!(buf, "{arg}")?;
        }
        tmp.write_all(&mut buf)?;
        Ok((cmd, tmp))
    }

    /// Builds a command from `ProcessBuilder` for everything but not `args`.
    fn build_command_without_args(&self) -> Command {
        let mut command = {
            let mut iter = self.wrappers.iter().rev().chain(once(&self.program));
            let mut cmd = Command::new(iter.next().expect("at least one `program` exists"));
            cmd.args(iter);
            cmd
        };

View on GitHub (pinned to 42eee92bc9)