ducaale/xh · error

JSON values are not supported in multipart fields

Error message

JSON values are not supported in multipart fields

What it means

When the request items form a multipart body, to_curl uses curl -F to describe the form; curl cannot express raw JSON values as field values in -F, and xh will not read files to inline them. So JSON-typed fields (=: and =@ JSON-file items) are rejected outright during translation.

Solutions

  1. Drop --multipart and send JSON fields normally (non-multipart) if no file upload is truly needed
  2. Replace the JSON field with a plain string field (=) if the server accepts it
  3. Pass the JSON as an uploaded file part (key@file.json) instead of an inline JSON value
  4. Send two requests: one multipart upload and one JSON API call, then translate each separately

Example fix

// before
xh --curl -f POST example.org/upload 'meta:={"version":2}' f@data.bin
// after
xh --curl -f POST example.org/upload 'meta@meta.json' f@data.bin
Defensive patterns

Strategy: validation

Validate before calling

fn multipart_has_json(items: &[RequestItem]) -> bool {
    items.iter().any(|i| matches!(
        i,
        RequestItem::JsonField(..) | RequestItem::JsonFieldFromFile(..)
    ))
}
if is_multipart && multipart_has_json(&items) {
    eprintln!("remove := / :=@ fields when using --multipart");
    std::process::exit(2);
}

Type guard

fn is_json_request_item(i: &RequestItem) -> bool {
    matches!(i, RequestItem::JsonField(..) | RequestItem::JsonFieldFromFile(..))
}

Try / catch

match result {
    Err(e) if e.to_string().contains("JSON values are not supported in multipart") => {
        eprintln!("convert := fields to = fields or use key@file.json");
    }
    r => r?,
}

Prevention

When it happens

Trigger: Running `xh --curl` (or --httpie/--print-curl translation) on a command that combines -f/--multipart with JSON fields, e.g. `xh -f POST url 'data:={"a":1}'` or a JsonFieldFromFile item in a multipart request.

Common situations: Users mixing --multipart with := JSON value syntax; converting an existing JSON-body request to multipart for file upload while keeping JSON fields; automated curl export of a mixed request.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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

Appendix: source

Thrown at src/to_curl.rs:384

    }

    if let Some(raw) = args.raw {
        if args.form {
            cmd.header("content-type", FORM_CONTENT_TYPE);
        } else {
            cmd.header("content-type", JSON_CONTENT_TYPE);
            cmd.header("accept", JSON_ACCEPT);
        }

        cmd.opt("-d", "--data");
        cmd.arg(raw);
    } else if args.request_items.is_multipart() {
        // We can't use .body() here because we can't look inside the multipart
        // form after construction and we don't want to actually read the files
        for item in args.request_items.items {
            match item {
                RequestItem::JsonField(..) | RequestItem::JsonFieldFromFile(..) => {
                    return Err(anyhow!("JSON values are not supported in multipart fields"));
                }
                RequestItem::DataField { key, value, .. } => {
                    cmd.opt("-F", "--form");
                    cmd.arg(format!("{key}={value}"));
                }
                RequestItem::DataFieldFromFile { key, value, .. } => {
                    cmd.opt("-F", "--form");
                    cmd.arg(format!("{key}=<{value}"));
                }
                RequestItem::FormFile {
                    key,
                    file_name,
                    file_type,
                    file_name_header,
                } => {
                    cmd.opt("-F", "--form");
                    let mut val = format!("{key}=@{file_name}");
                    if let Some(file_type) = file_type {

View on GitHub (pinned to 2404aceecc)