astrid-runtime/astrid · error
CORS origin uses scheme ; only http/https are valid for…
Error message
CORS origin {raw:?} uses scheme {other:?}; only http/https are valid for browser origins What it means
Validation guard in validate_cors_origin (run per cors_allow_origins entry at boot): the origin string parsed as a URL but its scheme is not http or https, so it can never match a browser Origin header, which only ever carries those schemes.
Solutions
- Change the cors_allow_origins entry to an http:// or https:// origin
- Remove ws:// or custom-scheme entries — WebSocket handshakes still originate from http(s) pages
- Drop non-URL entries like '*' and use the dedicated allow-all mechanism if intended
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at crates/astrid-gateway/src/config.rs:192 when the library encounters an invalid state.
Common situations: See trigger scenarios.
AI-assisted analysis of astrid-runtime/astrid@affd8760f4 (2026-09-09).
Data as JSON: /api/errors/7527cd63fcc73420.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-gateway/src/config.rs:192
crate::tls::warn_if_key_is_too_open(&tls.key_path);
}
Ok(())
}
}
/// Validate a single CORS origin string. Origins MUST be of the form
/// `scheme://host[:port]` with no path, query, or fragment — that's
/// what the browser sends in `Origin:` and what the response's
/// `Access-Control-Allow-Origin:` is byte-matched against. A
/// `https://app.example/` (trailing slash) would silently fail to
/// match a real preflight; rejecting it here is what makes that
/// surfacable.
fn validate_cors_origin(raw: &str) -> anyhow::Result<()> {
let parsed = url::Url::parse(raw)
.map_err(|e| anyhow::anyhow!("CORS origin {raw:?} doesn't parse as a URL: {e}"))?;
match parsed.scheme() {
"http" | "https" => {},
other => anyhow::bail!(
"CORS origin {raw:?} uses scheme {other:?}; only http/https are valid for browser origins"
),
}
if parsed.host_str().is_none() {
anyhow::bail!("CORS origin {raw:?} has no host component");
}
// Browsers strip userinfo before sending `Origin:`, so a config
// entry with embedded credentials can never match a real
// preflight. Reject so operators don't silently misconfigure.
if !parsed.username().is_empty() || parsed.password().is_some() {
anyhow::bail!(
"CORS origin {raw:?} carries userinfo (user:password); browsers strip it before sending `Origin:` so this can never match"
);
}
if parsed.path() != "" && parsed.path() != "/" {
anyhow::bail!(
"CORS origin {raw:?} carries a path ({:?}); origins are scheme+host+port only",
parsed.path()View on GitHub (pinned to affd8760f4)