Kuberwastaken/claurst · error · anyhow::Error

invalid bearer token header

Error message

invalid bearer token header: {}

What it means

bearer_header_value builds an HTTP `Authorization: Bearer <token>` header from a raw token string. HeaderValue::from_str rejects any string containing non-visible-ASCII bytes (control chars, non-ASCII). The library wraps that rejection in this error, meaning the supplied token itself is not a valid HTTP header value.

Solutions

  1. Trim whitespace and strip newlines from the token before passing it: token.trim()
  2. Verify the token source (env var, file, config) doesn't embed control or non-ASCII characters
  3. Ensure the value is a bearer token, not a certificate/multiline secret
  4. Log the token length and char classes (not the token) to find offending bytes

Example fix

// before
let header = bearer_header_value(&token)?;
// after
let header = bearer_header_value(token.trim())?;
Defensive patterns

Strategy: validation

Validate before calling

fn is_valid_header_value(s: &str) -> bool {
    s.bytes().all(|b| (32..=126).contains(&b) || b == b'\t')
}

Try / catch

match bearer_header_value(token.trim()) {
    Ok(h) => /* use h */,
    Err(e) => eprintln!("bad token encoding: {e}"),
}

Prevention

When it happens

Trigger: Calling bearer_header_value (directly or via MCP connect/auth flows) with a token containing newlines, CR/LF (header injection), NUL bytes, or non-ASCII UTF-8 characters (e.g. a password or pasted string with smart quotes).

Common situations: Token read from a malformed config file or env var with a trailing newline that wasn't trimmed; pasted token containing invisible whitespace; a token that is actually a multi-line PEM/secret rather than a bearer token.

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 Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/c066963d49a5ddd5. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/mcp/src/lib.rs:402

        /// Subscribe to raw JSON notifications from the transport.
        /// Returns an async stream of notification messages.
        ///
        /// For transports that natively support push notifications (e.g., WebSocket),
        /// this returns a stream that yields messages directly from the transport.
        /// For transports without native push support (e.g., stdio), this returns
        /// a stream that polls periodically.
        fn subscribe_to_notifications(
            &self,
        ) -> BoxStream<'static, anyhow::Result<serde_json::Value>>;

        fn protocol_version(&self) -> &'static str {
            LEGACY_PROTOCOL_VERSION
        }
    }

    pub(crate) fn bearer_header_value(token: &str) -> anyhow::Result<HeaderValue> {
        HeaderValue::from_str(&format!("Bearer {}", token))
            .map_err(|e| anyhow::anyhow!("invalid bearer token header: {}", e))
    }

    pub(crate) fn is_event_stream_response(response: &reqwest::Response) -> bool {
        response
            .headers()
            .get(CONTENT_TYPE)
            .and_then(|value| value.to_str().ok())
            .map(|value| value.contains("text/event-stream"))
            .unwrap_or(false)
    }

    pub(crate) fn resolve_legacy_endpoint(base_url: &str, endpoint: &str) -> anyhow::Result<String> {
        let endpoint = endpoint.trim();
        if endpoint.is_empty() {
            anyhow::bail!("legacy SSE endpoint event did not include a POST endpoint");
        }
        if let Ok(url) = url::Url::parse(endpoint) {
            return Ok(url.to_string());

View on GitHub (pinned to b0637c97ec)