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
- Trim the token before passing it in: token.trim().to_string() in the caller (or inside create_client before formatting).
- Validate the token is ASCII and printable before calling create_client (e.g. token.bytes().all(|b| b.is_ascii_graphic())).
- Fix the source of the token: trim when reading from file/env, or correct the secret in the secrets manager.
- 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
- Always .trim() tokens read from files or environment variables before passing to create_client.
- Validate tokens are printable ASCII at the configuration-loading boundary.
- Prefer library APIs returning Result over expect/unwrap when wrapping secret material into headers.
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
- unreachable, language is not supported: {language:#?}
- itered cannot be empty
- Failed to parse Cargo.toml.default
- client build
- cron: a panic occurred during find_next_occurrence
AI-assisted analysis of windmill-labs/windmill@e474e8803c (2026-09-03).
Data as JSON: /api/errors/2252b0762a6959b8.
Report an issue: GitHub.