astrid-runtime/astrid · error
CORS origin carries a path ( ); origins are…
Error message
CORS origin {raw:?} carries a path ({:?}); origins are scheme+host+port only What it means
Validation guard in validate_cors_origin: the entry contains a path component, but an origin is scheme+host+port only; browsers never send a path in Origin:, so the entry could never match and would silently break preflight handling.
Solutions
- Strip the path from the entry, keeping only scheme://host[:port]
- Move route-specific matching out of CORS config — CORS origins are not URL prefixes
- Re-test browser preflight after correction
Defensive patterns
Strategy: validation
When it happens
Trigger: Thrown at crates/astrid-gateway/src/config.rs:208 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/b0138940dc849b98.
Report an issue: GitHub.
Appendix: source
Thrown at crates/astrid-gateway/src/config.rs:208
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()
);
}
if parsed.query().is_some() || parsed.fragment().is_some() {
anyhow::bail!(
"CORS origin {raw:?} carries a query/fragment; origins are scheme+host+port only"
);
}
// Disallow trailing-slash forms — browsers send `https://app.example`
// (no slash) in `Origin:` and the response header is byte-matched.
if raw.ends_with('/') {
anyhow::bail!(
"CORS origin {raw:?} has a trailing slash; remove it (browsers send `Origin:` without one)"
);
}
// Reject a raw IDN — browsers transmit the Punycode (ASCII)
// form in `Origin:`, so the bytes wouldn't match anyway. TheView on GitHub (pinned to affd8760f4)