ducaale/xh · error

Can't read request from multiple files

Error message

Can't read request from multiple files

What it means

xh refuses to build a request body when more than one file would supply it. In body_from_file, multipart file items and the -f single-file body path all feed the single `body` variable; a second file item finds body already Some and aborts, because an HTTP request body can only have one source here.

Solutions

  1. Use only one file for the request body; remove the extra file argument
  2. If multiple parts are needed, use multipart form syntax: -f or key=value@file items instead of a bare body file
  3. Combine the files into one file first, or use stdin (e.g. `cat a b | xh POST url`)
  4. Check the constructed CLI/script for duplicated --body-file/@file arguments

Example fix

// before
xh POST example.org/upload @part1.bin @part2.bin
// after
xh -f POST example.org/upload part1@part1.bin part2@part2.bin
Defensive patterns

Strategy: validation

Validate before calling

// count file-sourced body parts before invoking
let file_args = std::env::args().filter(|a| a == "@" || a.starts_with('@')).count();
if file_args > 1 { eprintln!("pass at most one body file; use -f key@file for parts"); std::process::exit(2); }

Try / catch

match result {
    Err(e) if e.to_string().contains("multiple files") => eprintln!("Use -f key=value@file for multiple parts"),
    Err(e) => return Err(e),
    Ok(v) => Ok(v),
}

Prevention

When it happens

Trigger: Passing multiple standalone file arguments (or a request-items file entry after another file) so that `body` is already Some(Body::File{..}) when the next file is processed, e.g. `xh POST url @a.json @b.json` or combining a body file with a form file item.

Common situations: Users mimicking curl's multiple @file behavior; scripts appending extra file arguments by mistake; confusion between `-f` form fields (multiple allowed) and a single request body file.

Related errors


AI-assisted analysis of ducaale/xh@2404aceecc (2026-09-13). Data as JSON: /api/errors/369d28d51614af24. Report an issue: GitHub.

Appendix: source

Thrown at src/request_items.rs:429

        for item in self.items {
            match item {
                RequestItem::DataField { .. }
                | RequestItem::JsonField(..)
                | RequestItem::DataFieldFromFile { .. }
                | RequestItem::JsonFieldFromFile(..) => {
                    return Err(anyhow!(
                        "Request body (from a file) and request data (key=value) cannot be mixed."
                    ));
                }
                RequestItem::FormFile {
                    key,
                    file_name,
                    file_type,
                    file_name_header,
                } => {
                    assert!(key.is_empty());
                    if body.is_some() {
                        return Err(anyhow!("Can't read request from multiple files"));
                    }
                    body = Some(Body::File {
                        file_type: file_type
                            .as_deref()
                            .or_else(|| mime_guess::from_path(&file_name).first_raw())
                            .map(HeaderValue::from_str)
                            .transpose()?,
                        file_name: expand_tilde(file_name),
                        file_name_header,
                    });
                }
                RequestItem::HttpHeader(..)
                | RequestItem::HttpHeaderFromFile(..)
                | RequestItem::HttpHeaderToUnset(..)
                | RequestItem::UrlParam(..)
                | RequestItem::UrlParamFromFile(..) => {}
            }
        }

View on GitHub (pinned to 2404aceecc)