Y2Z/monolith · error

Invalid User-Agent header specified

Error message

Invalid User-Agent header specified

What it means

In `Session::new` (src/session.rs:33), when the caller supplies a `user_agent` in `MonolithOptions`, it is converted to a `HeaderValue` via `HeaderValue::from_str(user_agent).expect("Invalid User-Agent header specified")`. `from_str` rejects any string containing non-visible-ASCII characters (bytes outside 0x20–0x7E, plus DEL/0x7F). If the user-agent string contains such bytes (e.g. non-ASCII text, control characters, embedded newlines), the expect panics during session construction, aborting the whole program before any request is made.

Source

Thrown at src/session.rs:33

pub struct Session {
    cache: Option<Cache>,
    client: Client,
    cookies: Option<Vec<Cookie>>,
    pub options: MonolithOptions,
    urls: Vec<String>,
}

impl Session {
    pub fn new(
        cache: Option<Cache>,
        cookies: Option<Vec<Cookie>>,
        options: MonolithOptions,
    ) -> Self {
        let mut header_map = HeaderMap::new();
        if let Some(user_agent) = &options.user_agent {
            header_map.insert(
                USER_AGENT,
                HeaderValue::from_str(user_agent).expect("Invalid User-Agent header specified"),
            );
        }
        let client = Client::builder()
            .timeout(Duration::from_secs(if options.timeout > 0 {
                options.timeout
            } else {
                // We have to specify something that eventually makes the program fail
                // (prevent it from hanging forever)
                600 // 10 minutes in seconds
            }))
            .danger_accept_invalid_certs(options.insecure)
            .default_headers(header_map)
            .build()
            .expect("Failed to initialize HTTP client");

        Session {
            cache,
            cookies,

View on GitHub (pinned to a6fc8d0095)

Solutions

  1. Pass only ASCII user-agent strings (visible characters, no newlines): e.g. use the standard browser UA format.
  2. Sanitize/strip the value before constructing options: keep only bytes in 0x20–0x7E and trim whitespace.
  3. If the goal is a Unicode-looking UA, percent-style or transliterate it — HTTP header values must be visible ASCII.
  4. In wrapper scripts, validate the UA first: reject or filter it if it fails an ASCII-visible-charset check.
  5. Patch the code to handle the Result gracefully (fall back to the default UA and warn) instead of expect()-panicking.

Example fix

// before
options.user_agent = Some("Mønolith/1.0 🚀\n".to_string());
// Session::new panics: Invalid User-Agent header specified

// after
let ua: String = "Mønolith/1.0 🚀\n"
    .chars()
    .filter(|c| (*c as u32) >= 0x20 && (*c as u32) <= 0x7E)
    .collect();
options.user_agent = if ua.is_empty() {
    None // falls back to default UA
} else {
    Some(ua)
};
Defensive patterns

Strategy: validation

Validate before calling

// Validate the user agent before building MonolithOptions
fn is_valid_user_agent(ua: &str) -> bool {
    !ua.is_empty()
        && ua
            .bytes()
            .all(|b| (0x20..=0x7E).contains(&b))
        && !ua.starts_with(' ')
        && !ua.ends_with(' ')
}

// caller
let ua = cli.user_agent.unwrap_or_else(|| DEFAULT_USER_AGENT.to_string());
assert!(is_valid_user_agent(&ua), "user-agent must be visible ASCII");
options.user_agent = Some(ua);

Type guard

fn is_safe_header_value(s: &str) -> bool {
    s.bytes().all(|b| (0x20..=0x7E).contains(&b))
}

// usage
if let Some(ua) = &options.user_agent {
    if !is_safe_header_value(ua) {
        eprintln!("ignoring invalid user-agent (must be visible ASCII)");
        options.user_agent = None; // fall back to default
    }
}

Try / catch

match HeaderValue::from_str(user_agent) {
    Ok(v) => { header_map.insert(USER_AGENT, v); }
    Err(_) => {
        eprintln!("warning: invalid user-agent, using default");
        header_map.insert(USER_AGENT, HeaderValue::from_static(DEFAULT_USER_AGENT));
    }
}

Prevention

When it happens

Trigger: Calling `Session::new` with `MonolithOptions.user_agent = Some(...)` where the value contains non-ASCII (Unicode) characters, control characters (e.g. \n, \r, \t, \0), or other bytes that violate HTTP header-value visibility rules. Via CLI: `monolith --user-agent "…" <url>` with a UA string pasted containing smart quotes, emoji, CJK text, or a stray newline from a shell variable.

Common situations: Pasting a UA string from a browser devtools export that includes non-ASCII characters; shell variables with trailing control characters or a newline; localized scripts that embed unicode text in the UA; config files read with wrong encoding (UTF-16/BOM bytes leaking in); programmatically constructing options from user input without sanitization.

Related errors


AI-assisted analysis of Y2Z/monolith@a6fc8d0095 (2026-09-05). Data as JSON: /api/errors/81fda8fb7b8ca68f. Report an issue: GitHub.