ducaale/xh · error
Invalid UTF-8
Error message
Invalid UTF-8
What it means
The curl translation layer builds a command line whose -o/--output argument must be a valid UTF-8 string. Path arguments on Unix can be arbitrary bytes; when args.output is not valid UTF-8, translate returns this error instead of emitting a malformed curl command.
Solutions
- Rename the output file to valid UTF-8 before translating
- Run with a UTF-8 locale (e.g. LANG=C.UTF-8) so paths decode correctly
- Fix the source of the filename (e.g. ls/find output) to produce valid UTF-8
- Skip curl translation for that invocation and download directly with xh
Example fix
// before xh --curl -o "$RAW_BYTES_NAME" https://example.org // after export LANG=C.UTF-8 xh --curl -o output.bin https://example.org
Defensive patterns
Strategy: validation
Validate before calling
if !std::str::from_utf8(output_path.as_os_str().as_encoded_bytes()).is_ok() {
eprintln!("output filename must be valid UTF-8 for --curl translation");
std::process::exit(2);
} Type guard
fn is_utf8_path(p: &std::path::Path) -> bool {
p.to_str().is_some()
} Try / catch
match result {
Err(e) if e.to_string() == "Invalid UTF-8" => {
eprintln!("rename output file to valid UTF-8");
}
r => r?,
} Prevention
- Keep filenames ASCII/UTF-8 when scripting around xh
- Run in a UTF-8 locale (LANG=C.UTF-8)
- Avoid passing raw byte filenames from ls/find output
When it happens
Trigger: Passing -o/--output a filename containing invalid UTF-8 bytes (common with OS-supplied names from a non-UTF-8 locale or corrupted filenames), then running `xh --curl`/print_curl_translation.
Common situations: Linux systems with filenames that are raw bytes from another encoding (Latin-1, SJIS); shell variables expanded from file listings with mojibake; scripts on systems without UTF-8 locale.
Understand the failure class
Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- is not a supported encoding, please refer to…
- JSON values are not supported in multipart fields
- -I/--head is incompatible with sending data. Consider…
- Inferred port number in --resolve from request URL.
- Ignored
AI-assisted analysis of ducaale/xh@2404aceecc (2026-09-13).
Data as JSON: /api/errors/986193a8b8a86938.
Report an issue: GitHub.
Appendix: source
Thrown at src/to_curl.rs:176
cmd.opt("-N", "--no-buffer");
}
// Since --fail is more disruptive than HTTPie's --check-status flag, we will not enable
// it unless the user explicitly sets the latter flag
if args.check_status == Some(true) {
// Suppresses output on failure, unlike us
cmd.opt("-f", "--fail");
}
// HTTP options
if args.follow {
cmd.opt("-L", "--location");
}
if let Some(num) = args.max_redirects {
cmd.arg("--max-redirs");
cmd.arg(num.to_string());
}
if let Some(filename) = args.output {
let filename = filename.to_str().ok_or_else(|| anyhow!("Invalid UTF-8"))?;
cmd.opt("-o", "--output");
cmd.arg(filename);
} else if args.download {
cmd.opt("-O", "--remote-name");
}
if args.resume {
cmd.opt("-C", "--continue-at");
cmd.arg("-"); // Tell curl to guess, like we do
}
match args.verify.unwrap_or(Verify::Yes) {
Verify::CustomCaBundle(filename) => {
cmd.arg("--cacert");
cmd.arg(filename);
}
Verify::No => {
cmd.opt("-k", "--insecure");
}
Verify::Yes => {}View on GitHub (pinned to 2404aceecc)