seanmonstar/warp · error
Origin is always a valid HeaderValue
Error message
Origin is always a valid HeaderValue
What it means
Cors::allow_origins converts each origin to a string and parses it as a HeaderValue, expecting success always ('Origin is always a valid HeaderValue'). This expect panics only if an origin string somehow isn't a valid header value — practically unreachable via valid Origin inputs, but reachable through the &str IntoOrigin path's own panics for malformed origins.
Solutions
- Pass bare origins like "https://example.com" without paths, queries, or trailing slashes
- Validate each origin string parses as an Origin before building the CORS layer
- Prefer allow_origins with typed Origin values where available
Example fix
// before cors().allow_origins(&["https://example.com/api"]) // after cors().allow_origins(&["https://example.com"])
Defensive patterns
Strategy: validation
Validate before calling
fn is_bare_origin(s: &str) -> bool {
let mut parts = s.splitn(2, "://");
parts.next().map_or(false, |sch| !sch.is_empty()) && parts.next().map_or(false, |rest| !rest.is_empty() && !rest.contains('/'))
} Prevention
- Pass bare scheme://host origins without paths or queries
- Validate origin strings at config-load time
- Prefer typed Origin inputs over raw strings
When it happens
Trigger: cors().allow_origins(&["bad origin"]) with strings failing Origin parsing (see allow_origin's 'missing scheme'/'invalid Origin' panics); direct panic at this line is nearly impossible with well-formed origins.
Common situations: Passing full URLs with paths ('https://x.com/path') or malformed origins to allow_origins; config entries that aren't bare origins.
Understand the failure class
Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.
Related errors
AI-assisted analysis of seanmonstar/warp@ff34d7213e (2026-09-09).
Data as JSON: /api/errors/d5ebe4bb0d2248c9.
Report an issue: GitHub.
Appendix: source
Thrown at src/filters/cors.rs:226
/// Add multiple origins to the existing list of allowed `Origin`s.
///
/// # Panics
///
/// Panics if the provided argument is not a valid `Origin`.
pub fn allow_origins<I>(mut self, origins: I) -> Self
where
I: IntoIterator,
I::Item: IntoOrigin,
{
let iter = origins
.into_iter()
.map(IntoOrigin::into_origin)
.map(|origin| {
origin
.to_string()
.parse()
.expect("Origin is always a valid HeaderValue")
});
self.origins.get_or_insert_with(HashSet::new).extend(iter);
self
}
/// Sets the `Access-Control-Max-Age` header.
///
/// # Example
///
///
/// ```
/// use std::time::Duration;
/// use warp::Filter;
///
/// let cors = warp::cors()
/// .max_age(30) // 30u32 secondsView on GitHub (pinned to ff34d7213e)