hatoo/oha · error · anyhow::Error

Parse header

Error message

Parse header

What it means

parse_header converts a CLI header string like 'Name: value' into an http HeaderName/HeaderValue pair. It splits on the first ':' with splitn(2); if no colon is present the ensure fails with 'Parse header'. Invalid header names or values also fail, but at the subsequent from_str calls with different errors.

Solutions

  1. Format each header as `Name: value`, e.g. `-H "Authorization: Bearer xyz"`.
  2. Quote the argument so the shell preserves the colon-containing string.
  3. Remember the value is trimmed of leading spaces, so extra spaces after the colon are fine.

Example fix

// before
oha -H Authorization Bearer xyz https://example.com
// after
oha -H "Authorization: Bearer xyz" https://example.com
Defensive patterns

Strategy: validation

Validate before calling

fn valid_header_arg(s: &str) -> bool {
    s.contains(':')
}

Type guard

fn split_header(s: &str) -> Option<(&str, &str)> {
    s.split_once(':')
}

Try / catch

match parse_header(input) {
    Ok((name, value)) => /* use header */,
    Err(e) if e.to_string().contains("Parse header") => {
        eprintln!("Headers must be 'Name: value'; got: {input}");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Passing a -H/--header value without a colon, e.g. `-H Authorization` or `-H "Bearer xyz"`.

Common situations: Users separating the header name and value with a space instead of a colon; copying headers from a browser devtools 'name value' display; forgetting quotes so the shell splits the colon-containing string.

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.

Related errors


AI-assisted analysis of hatoo/oha@4efba2d113 (2026-09-09). Data as JSON: /api/errors/9fde19ea4355a351. Report an issue: GitHub.

Appendix: source

Thrown at src/cli.rs:6

use hyper::http::header::{HeaderName, HeaderValue};
use std::str::FromStr;

pub fn parse_header(s: &str) -> Result<(HeaderName, HeaderValue), anyhow::Error> {
    let header = s.splitn(2, ':').collect::<Vec<_>>();
    anyhow::ensure!(header.len() == 2, anyhow::anyhow!("Parse header"));
    let name = HeaderName::from_str(header[0])?;
    let value = HeaderValue::from_str(header[1].trim_start_matches(' '))?;
    Ok::<(HeaderName, HeaderValue), anyhow::Error>((name, value))
}

pub fn parse_n_requests(s: &str) -> Result<usize, String> {
    let s = s.trim().to_lowercase();
    if let Some(num) = s.strip_suffix('k') {
        num.parse::<f64>()
            .map(|n| (n * 1000_f64) as usize)
            .map_err(|e| e.to_string())
    } else if let Some(num) = s.strip_suffix('m') {
        num.parse::<f64>()
            .map(|n| (n * 1_000_000_f64) as usize)
            .map_err(|e| e.to_string())
    } else {
        s.parse::<usize>().map_err(|e| e.to_string())
    }

View on GitHub (pinned to 4efba2d113)