{"record":{"id":"89f52fd6b012ba1d","repo":"jdx/mise","slug":"unsupported-relay-destination","errorCode":null,"errorMessage":"unsupported relay destination","messagePattern":"unsupported relay destination","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/github_relay.rs","lineNumber":980,"sourceCode":"            _ = hangup.recv() => Err(crate::request_exit(129)),\n            result = operation => result,\n        }\n    }\n\n    /// 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","sourceCodeStart":962,"sourceCodeEnd":998,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/github_relay.rs#L962-L998","documentation":"The relay's request() adapter only accepts https URLs to GitHub hosts without credentials or a custom port. If the URL's scheme isn't https, carries a username/password, or specifies an explicit port, it's rejected up front with \"unsupported relay destination\" so the relay never proxies non-HTTPS or credentialed endpoints.","triggerScenarios":"Calling request (or code paths like adapters_use_the_brokers_timeout_policy that use it) with a URL that is http://, includes user:pass@, or has :port — e.g. an http://github.com mirror, a proxy URL with embedded credentials, or an enterprise/ghes host with a port.","commonSituations":"MIS-configured github enterprise endpoints with ports; HTTP fallback URLs in scripts; credentials pasted into download URLs; custom reverse-proxy hosts on nonstandard ports.","solutions":["Use a plain https URL with no credentials and no port, e.g. https://api.github.com/... or https://github.com/...","Move credentials out of the URL into headers/auth configuration handled by the relay itself.","Remove an explicit :port (443 is implied for https); if you need a nonstandard endpoint, don't route it through the relay."],"exampleFix":"// before\nlet url = Url::parse(\"https://github.com:8443/foo/bar/releases/download/v1/a.zip\")?;\n// after\nlet url = Url::parse(\"https://github.com/foo/bar/releases/download/v1/a.zip\")?;","handlingStrategy":"validation","validationCode":"fn relay_supported(url: &Url) -> bool {\n    url.scheme() == \"https\"\n        && url.username().is_empty()\n        && url.password().is_none()\n        && url.port().is_none()\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Always construct GitHub URLs as https with no userinfo and no explicit port.","Keep credentials in headers/config, never in the URL.","Route nonstandard endpoints (GHES, proxies) through a plain HTTP client, not the relay."],"tags":["url","validation","relay","security"],"backgroundTag":"invalid-url-format","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"}