Kuberwastaken/claurst · critical

unable to parse api key for http header

Error message

unable to parse api key for http header

What it means

MinimaxProvider::new inserts the API key as an X-Api-Key default header using HeaderValue::from_str(&api_key).expect(...). HTTP header values must contain only visible ASCII (bytes 0x20–0x7E, plus a few others); if the key contains whitespace control chars, newlines, or non-ASCII bytes, from_str fails and the provider panics with this message.

Solutions

  1. Trim whitespace/newlines from the API key before constructing the provider: api_key.trim().
  2. Verify the key with `od -c` or similar to detect hidden control characters or non-ASCII bytes.
  3. Regenerate/copy the key directly from the provider dashboard without transformation (no base64, no wrapping).
  4. If constructing in your own code, validate with HeaderValue::from_str first and return a proper error instead of panicking.

Example fix

// before
let provider = MinimaxProvider::new(std::env::var("MINIMAX_API_KEY")?);
// after
let key = std::env::var("MINIMAX_API_KEY")?;
let key = key.trim().to_string();
header::HeaderValue::from_str(&key)
    .context("MINIMAX_API_KEY contains invalid characters for an HTTP header")?;
let provider = MinimaxProvider::new(key);
Defensive patterns

Strategy: validation

Validate before calling

fn valid_header_value(s: &str) -> bool {
    !s.is_empty() && s.bytes().all(|b| (0x20..=0x7E).contains(&b) || b == 0x09)
}
// before constructing:
let key = api_key.trim();
if !valid_header_value(key) { return Err(anyhow!("MINIMAX_API_KEY contains characters invalid in an HTTP header")); }

Type guard

fn as_header_value(s: &str) -> Option<reqwest::header::HeaderValue> {
    reqwest::header::HeaderValue::from_str(s.trim()).ok()
}

Try / catch

// this panic cannot be caught in Rust; pre-validate instead
let key = api_key.trim();
match reqwest::header::HeaderValue::from_str(key) {
    Ok(_) => MinimaxProvider::new(key.to_string()),
    Err(e) => return Err(anyhow!("invalid MINIMAX_API_KEY header value: {e}")),
}

Prevention

When it happens

Trigger: Passing an api_key containing non-ASCII or control characters (e.g. a newline from a mis-trimmed env var, a paste with trailing CR, or a UTF-8 multi-byte character) into MinimaxProvider::new.

Common situations: Copying an API key with a trailing newline into an env file; shell quoting adding whitespace; using a base64-encoded or otherwise transformed key that includes padding/newlines; terminal paste artifacts.

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/a5ed613d19207b22. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/api/src/providers/minimax.rs:37

};
use crate::types::{ApiMessage, ApiToolDefinition, CreateMessageRequest, ThinkingConfig};

use super::message_normalization::normalize_anthropic_messages;

pub struct MinimaxProvider {
    http_client: Client,
    api_key: String,
    api_base: String,
    service_tier: Option<String>,
    id: ProviderId,
}

impl MinimaxProvider {
    pub fn new(api_key: String) -> Self {
        let api_base = std::env::var("MINIMAX_BASE_URL")
            .unwrap_or_else(|_| claurst_core::constants::MINIMAX_ANTHROPIC_API_BASE.to_string());
        let mut headers = header::HeaderMap::new();
        headers.insert("X-Api-Key", header::HeaderValue::from_str(&api_key).expect("unable to parse api key for http header"));
        let http_client = Client::builder()
            .default_headers(headers)
            .timeout(crate::request_timeout())
            .build()
            .expect("MinimaxProvider: failed to build HTTP client");

        Self {
            http_client,
            api_key,
            api_base,
            service_tier: None,
            id: ProviderId::new(ProviderId::MINIMAX),
        }
    }

    pub fn with_base_url(mut self, api_base: impl Into<String>) -> Self {
        self.api_base = api_base.into();
        self

View on GitHub (pinned to b0637c97ec)