windmill-labs/windmill · error

header creation

Error message

header creation

What it means

create_client builds a Bearer Authorization header from the given token via HeaderValue::from_str and unwraps it with expect("header creation"). HeaderValue::from_str fails when the formatted string contains bytes that are not visible ASCII (0x20-0x7E) — e.g. control characters, newlines, or non-ASCII. A token read from env/config that contains a trailing newline or stray whitespace/control byte panics here.

Source

Thrown at backend/windmill-api-client/src/lib.rs:176

    /// List workspaces
    pub async fn list_workspaces(&self) -> Result<Vec<types::Workspace>, Error> {
        let url = format!("{}/workspaces/list", self.baseurl);
        let response = self.client.get(&url).send().await?;

        if response.status().is_success() {
            Ok(response.json().await?)
        } else {
            Err(Error::UnexpectedResponse(
                response.status().as_u16(),
                response.text().await.unwrap_or_default(),
            ))
        }
    }
}

/// Create a client with bearer token authentication
pub fn create_client(base_url: &str, token: String) -> Client {
    let mut val = HeaderValue::from_str(&format!("Bearer {token}")).expect("header creation");
    val.set_sensitive(true);
    let mut headers = HeaderMap::new();
    headers.insert(AUTHORIZATION, val);
    let client = reqwest::ClientBuilder::new()
        .default_headers(headers)
        .build()
        .expect("client build");
    Client::new_with_client(&format!("{}/api", base_url.trim_end_matches('/')), client)
}

/// Error type for API client
#[derive(Debug)]
pub enum Error {
    /// Request error
    Request(reqwest::Error),
    /// Unexpected response status
    UnexpectedResponse(u16, String),
}

View on GitHub (pinned to e474e8803c)

Solutions

  1. Trim the token before passing it in: token.trim().to_string() in the caller (or inside create_client before formatting).
  2. Validate the token is ASCII and printable before calling create_client (e.g. token.bytes().all(|b| b.is_ascii_graphic())).
  3. Fix the source of the token: trim when reading from file/env, or correct the secret in the secrets manager.
  4. If you control the library, replace .expect with proper error propagation (return Result<Client, Error>) so a bad token yields a descriptive error instead of a panic.

Example fix

// before
let client = create_client(url, std::fs::read_to_string("token")?);

// after
let token = std::fs::read_to_string("token")?.trim().to_string();
let client = create_client(url, token);
Defensive patterns

Strategy: validation

Validate before calling

fn valid_token(token: &str) -> bool {
    token.bytes().all(|b| b.is_ascii_graphic()) && !token.is_empty()
}
// call: if !valid_token(&token) { bail!("token contains invalid header characters"); }

Type guard

fn sanitize_header_token(raw: &str) -> Option<String> {
    let t = raw.trim();
    (!t.is_empty() && t.bytes().all(|b| (0x21..=0x7e).contains(&b))).then(|| t.to_string())
}

Prevention

When it happens

Trigger: Calling create_client(base_url, token) where format!("Bearer {token}") yields invalid header bytes: a token containing \n or \r (common when read from a file/env without trimming), non-ASCII characters, or control characters.

Common situations: Token loaded from a file that ends with a newline (e.g. `cat token.txt` or fs::read_to_string without trim); secrets managers returning values with trailing whitespace; copy-pasted tokens containing invisible characters; misconfigured env var holding a multiline value.

Related errors


AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03). Data as JSON: /api/errors/2252b0762a6959b8. Report an issue: GitHub.