ducaale/xh · error
Cannot build a multipart request body from stdin
Error message
Cannot build a multipart request body from stdin
What it means
When stdin is piped into the CLI while --multipart is set, the body would have to come from stdin, but a multipart body cannot be constructed from raw stdin bytes together with the multipart form framing. The CLI refuses rather than producing a broken request.
Solutions
- Pass the file as a request item (e.g. file_field@path) instead of piping it via stdin.
- Add --ignore-stdin so the CLI ignores the piped stdin and builds the multipart body from request items only.
- Redirect stdin from the terminal or run in an interactive shell.
- Drop --multipart if you actually want a raw stdin body.
Example fix
// before cat data.bin | xh --multipart POST example.org // after xh --multipart POST example.org file@data.bin
Defensive patterns
Strategy: validation
Validate before calling
if !std::io::stdin().is_terminal() && !ignore_stdin && multipart_enabled {
eprintln!("stdin is piped; pass --ignore-stdin or use field@file for multipart");
std::process::exit(2);
} Try / catch
match run(args) {
Err(e) if e.to_string().contains("multipart request body from stdin") => {
eprintln!("tip: use --ignore-stdin or pass files as request items");
}
other => other,
} Prevention
- In scripts, always pass --ignore-stdin unless you intend to pipe a body.
- Send multipart files via field@path request items, never via stdin pipes.
- Redirect stdin from /dev/null in CI jobs.
- Remember --multipart changes what an 'empty' body means.
When it happens
Trigger: run() detects use_stdin (stdin piped and not --ignore-stdin), args.request_items.is_body_empty() is false or multipart forces entry, and args.multipart is true; then this error is returned before building the request.
Common situations: Piping a file into the command while also passing --multipart; scripting usage like `cat file | xh --multipart POST url`; forgetting --ignore-stdin in CI where stdin is a pipe.
Related errors
- Request body (from stdin) and request data (key=value)…
- Request body from stdin and --raw cannot be mixed. Pass…
- JSON values are not supported in multipart fields
- Message signature components require both…
- JSON values are not supported in Form fields
AI-assisted analysis of ducaale/xh@2404aceecc (2026-09-13).
Data as JSON: /api/errors/91cb0554150f2958.
Report an issue: GitHub.
Appendix: source
Thrown at src/main.rs:120
return Ok(ExitCode::SUCCESS);
}
if args.curl {
to_curl::print_curl_translation(args)?;
return Ok(ExitCode::SUCCESS);
}
let (mut headers, headers_to_unset) = args.request_items.headers()?;
let url = url_with_query(args.url, &args.request_items.query()?);
log::debug!("Complete URL: {url}");
let use_stdin = !(args.ignore_stdin || io::stdin().is_terminal() || test_pretend_term());
let body = if use_stdin {
if !args.request_items.is_body_empty() {
if args.multipart {
// Multipart bodies are never "empty", so we can get here without request items
return Err(anyhow!("Cannot build a multipart request body from stdin"));
} else {
return Err(anyhow!(
"Request body (from stdin) and request data (key=value) cannot be mixed. \
Pass --ignore-stdin to ignore standard input."
));
}
}
if args.raw.is_some() {
return Err(anyhow!(
"Request body from stdin and --raw cannot be mixed. \
Pass --ignore-stdin to ignore standard input."
));
}
let mut buffer = Vec::new();
io::stdin().read_to_end(&mut buffer)?;
Body::Raw(buffer)
} else if let Some(raw) = args.raw {
Body::Raw(raw.into_bytes())View on GitHub (pinned to 2404aceecc)