{"record":{"id":"44347dec3996dbc2","repo":"jdx/mise","slug":"unsupported-relay-host","errorCode":null,"errorMessage":"unsupported relay host","messagePattern":"unsupported relay host","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/github_relay.rs","lineNumber":985,"sourceCode":"    /// API adapter: an HTTP request over the forwarded private socket. No upstream\n    /// authentication headers are sent to the target or supplied by the target.\n    pub(crate) async fn request(\n        socket: &Path,\n        method: Method,\n        url: &Url,\n        headers: &http::HeaderMap,\n    ) -> Result<reqwest::Response> {\n        if url.scheme() != \"https\"\n            || !url.username().is_empty()\n            || url.password().is_some()\n            || url.port().is_some()\n        {\n            bail!(\"unsupported relay destination\");\n        }\n        let prefix = match url.host_str() {\n            Some(\"api.github.com\") => \"api\",\n            Some(\"github.com\") => \"web\",\n            _ => bail!(\"unsupported relay host\"),\n        };\n        let mut relay_url = Url::parse(&format!(\"http://localhost/{prefix}{}\", url.path()))?;\n        relay_url.set_query(url.query());\n        let (client, request_timeout) = adapter_client(socket).await?;\n        let mut req = client.request(method, relay_url);\n        for name in [\"accept\", \"range\", \"if-range\"] {\n            if let Some(value) = headers.get(name) {\n                req = req.header(name, value);\n            }\n        }\n        send_adapter_request(req, request_timeout).await\n    }\n\n    // Obtain the initiating machine's policy, not the target's saved settings.\n    // Discovery itself has a short fixed bound and bypasses broker request slots.\n    async fn adapter_client(socket: &Path) -> Result<(Client, Duration)> {\n        let builder = || {\n            Client::builder()","sourceCodeStart":967,"sourceCodeEnd":1003,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/github_relay.rs#L967-L1003","documentation":"After passing the scheme/port/credential checks, request() maps the URL host to a relay path prefix: api.github.com maps to /api and github.com maps to /web. Any other host — including GitHub Enterprise, mirrors, or typos — has no mapping and is rejected with \"unsupported relay host\". The relay can only proxy the two canonical github.com hosts.","triggerScenarios":"Calling request with a host like ghe.example.com, codeload.github.com, objects.githubusercontent.com, raw.githubusercontent.com, or a misspelled github.com — anything other than exactly api.github.com or github.com.","commonSituations":"Following redirect URLs to asset/CDN hosts and passing those into request(); configuring a GHES instance expecting relay support; typos or localized mirror hosts in config; constructing URLs from user input that lands on non-API hosts.","solutions":["Use only https://api.github.com or https://github.com URLs with the relay; resolve asset downloads through the API instead of hitting CDN hosts directly.","Correct host typos or replace mirror/GHES hosts with canonical github.com endpoints.","If you need another host, fetch it with a plain HTTP client rather than the relay."],"exampleFix":"// before\nlet url = Url::parse(\"https://raw.githubusercontent.com/foo/bar/main/file.txt\")?;\n// after\nlet url = Url::parse(\"https://api.github.com/repos/foo/bar/contents/file.txt\")?;","handlingStrategy":"validation","validationCode":"fn relay_host_prefix(url: &Url) -> Option<&'static str> {\n    match url.host_str()? {\n        \"api.github.com\" => Some(\"api\"),\n        \"github.com\" => Some(\"web\"),\n        _ => None,\n    }\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Only send api.github.com and github.com URLs to the relay.","Resolve asset/CDN downloads via the API endpoints rather than following raw hosts.","Normalize user/config-supplied GitHub URLs to canonical hosts before relaying."],"tags":["url","relay","validation","github"],"backgroundTag":"invalid-url","analyzedSha":"afd2eddd3a50c16190efc1c7e94404b48f72af57","analyzedAt":"2026-09-09T01:38:25.179Z","contentChangedAt":"2026-09-09T01:38:25.179Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}