seanmonstar/reqwest · error · BadScheme

URL scheme is not allowed

Error message

URL scheme is not allowed

What it means

The BadScheme struct (src/error.rs:432-441) displays as 'URL scheme is not allowed' and is raised by url_bad_scheme() (src/error.rs:387-389) as a Kind::Builder error carrying the offending Url. It is produced at three sites: IntoUrl::into_url() when a parsed Url has no host (src/into_url.rs:34-38), Client::execute_request() when the scheme is not http or https, or when https_only mode rejects an http URL (src/async_impl/client.rs:2622-2629), and the redirect policy when a Location header points to a non-http(s) scheme (src/redirect.rs:320-328). reqwest intentionally restricts transport to http/https; file, ftp, data, blob, ws schemes are rejected.

Solutions

  1. Use an absolute http:// or https:// URL with a host: prepend the scheme and host to relative paths before calling reqwest.
  2. If you enabled .https_only(true), either disable it or change the target URL to https://.
  3. For file:// resources use std::fs / tokio::fs instead of reqwest; for data: URIs parse them directly.
  4. Configure a redirect::Policy::none() or a custom policy that stops on cross-scheme redirects instead of erroring, if a server may redirect to a non-http(s) Location.

Example fix

// before
let resp = reqwest::get("/api/v1/users").await?;

// after
let base = std::env::var("API_BASE").unwrap_or_else(|_| "https://api.example.com".into());
let resp = reqwest::get(format!("{base}/api/v1/users")).await?;
Defensive patterns

Strategy: validation

Validate before calling

fn validate_reqwest_url(raw: &str) -> Result<url::Url, String> {
    let url = url::Url::parse(raw).map_err(|e| format!("invalid URL: {e}"))?;
    if !url.has_host() {
        return Err("URL must have a host".into());
    }
    match url.scheme() {
        "http" | "https" => Ok(url),
        other => Err(format!("scheme '{other}' not allowed by reqwest")),
    }
}

let url = validate_reqwest_url(&raw)?;
let resp = reqwest::get(url).await?;

Type guard

fn is_transport_scheme(url: &url::Url) -> bool {
    url.has_host() && matches!(url.scheme(), "http" | "https")
}

Try / catch

match reqwest::get(url).await {
    Ok(resp) => { /* ... */ }
    Err(e) => {
        if e.is_builder()
            && e.source().map(|s| s.to_string()).as_deref() == Some("URL scheme is not allowed")
        {
            // reject the input URL as unsupported transport
        } else {
            return Err(e);
        }
    }
}

Prevention

When it happens

Trigger: Calling reqwest::get("file:///etc/hosts"), get("data:text/plain,hi"), get("ftp://host"), get("blob:https://...") (non-wasm), or a bare relative path like get("/api/v1") with no host. Setting .https_only(true) on the builder and then requesting an http:// URL. Following a redirect whose Location is a non-http(s) URI (e.g. a server redirecting to an app:// deep link or data: URI). On non-wasm, passing a host-less Url through IntoUrl.

Common situations: Reading a URL from an env var or config file that was set to a file:// path during local dev. Putting a relative path in a base-URL variable. Mixed-content: an https-only client pointed at an http upstream behind a proxy. A server returning a cross-scheme redirect (https -> app deep link). Test fixtures using data: URIs.

Related errors


AI-assisted analysis of seanmonstar/reqwest@9f06fd28ab (2026-08-10). Data as JSON: /api/errors/f97a850abae2bbd0. Report an issue: GitHub.

Appendix: source

Thrown at src/error.rs:388

    status: StatusCode,
    #[cfg(not(all(target_arch = "wasm32", any(target_os = "unknown", target_os = "none"))))] reason: Option<hyper::ext::ReasonPhrase>,
) -> Error {
    Error::new(
        Kind::Status(
            status,
            #[cfg(not(all(
                target_arch = "wasm32",
                any(target_os = "unknown", target_os = "none")
            )))]
            reason,
        ),
        None::<Error>,
    )
    .with_url(url)
}

pub(crate) fn url_bad_scheme(url: Url) -> Error {
    Error::new(Kind::Builder, Some(BadScheme)).with_url(url)
}

pub(crate) fn url_invalid_uri(url: Url) -> Error {
    Error::new(Kind::Builder, Some("Parsed Url is not a valid Uri")).with_url(url)
}

if_wasm! {
    pub(crate) fn wasm(js_val: wasm_bindgen::JsValue) -> BoxError {
        format!("{js_val:?}").into()
    }
}

pub(crate) fn upgrade<E: Into<BoxError>>(e: E) -> Error {
    Error::new(Kind::Upgrade, Some(e))
}

// io::Error helpers

View on GitHub (pinned to 9f06fd28ab)