{"record":{"id":"dddbb7c3298a9f5f","repo":"jdx/mise","slug":"got-html-instead-of-text-from-url","errorCode":null,"errorMessage":"Got HTML instead of text from {url}","messagePattern":"Got HTML instead of text from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/http.rs","lineNumber":1560,"sourceCode":"            .client\n            .send_with_https_fallback_with_retries(\n                Method::GET,\n                url.clone(),\n                &headers,\n                \"GET\",\n                self.retries,\n                true,\n            )\n            .await?;\n        let text = resp.text().await?;\n        if text.starts_with(\"<!DOCTYPE html>\") {\n            if url.scheme() == \"http\" {\n                // try with https since http may be blocked\n                url.set_scheme(\"https\").unwrap();\n                self.url = Ok(url);\n                return Box::pin(self.send()).await;\n            }\n            bail!(\"Got HTML instead of text from {}\", url);\n        }\n        Ok(text)\n    }\n}\n\nfn is_github_forbidden(url: &Url, resp: &Response) -> bool {\n    resp.status() == StatusCode::FORBIDDEN && url.host_str() == Some(\"api.github.com\")\n}\n\nfn is_github_unauthorized(url: &Url, resp: &Response) -> bool {\n    resp.status() == StatusCode::UNAUTHORIZED && crate::github::is_github_api_url(url)\n}\n\n/// Maximum body bytes buffered when building a GitHub error report, so an\n/// oversized or slow-trickling error response can't exhaust memory. The overall\n/// request timeout bounds the time; this bounds the memory.\nconst MAX_ERROR_BODY_BYTES: usize = 64 * 1024;\n","sourceCodeStart":1542,"sourceCodeEnd":1578,"githubUrl":"https://github.com/jdx/mise/blob/afd2eddd3a50c16190efc1c7e94404b48f72af57/src/http.rs#L1542-L1578","documentation":"mise fetched a URL expecting plain text but the response was detected as HTML. For http:// URLs mise automatically retries over https://; if the URL is already https (or the retry also returned HTML), it fails with this error. Typically the endpoint returned an error page, login page, or proxy block page instead of the expected text payload.","triggerScenarios":"Requesting a plain-text resource (e.g. a version list or config file) with HttpResource::text / get_text where the response body is HTML; http:// URL retried over https:// still yields HTML; a captive portal or proxy serves an HTML interstitial.","commonSituations":"Corporate proxy or captive portal intercepting requests; pointing mise at a URL that serves an HTML dashboard instead of raw text (e.g. a GitHub HTML page instead of a raw file); a mirror returning 200 with an HTML error page.","solutions":["Use the raw-content URL (e.g. raw.githubusercontent.com instead of github.com) so the server returns text, not HTML.","Bypass the intercepting proxy/captive portal: fix proxy env vars (HTTPS_PROXY/NO_PROXY) or authenticate to the network.","Check that the custom mirror/backend URL is correct and serves plain text."],"exampleFix":"// before\nMISE_NODE_MIRROR=https://github.com/nodejs/node/releases/download\n// after: point at an endpoint serving plain text/real artifacts\nMISE_NODE_MIRROR=https://nodejs.org/dist","handlingStrategy":"validation","validationCode":"let body = client.get(url).send().await?.text().await?;\nlet looks_html = body.trim_start().starts_with('<')\n    && (body.contains(\"<html\") || body.contains(\"<!DOCTYPE html\"));\nif looks_html { return Err(\"endpoint returned HTML; check URL/proxy\".into()); }","typeGuard":"fn is_html(body: &str) -> bool {\n    let t = body.trim_start();\n    (t.starts_with('<') && (t.contains(\"<html\") || t.contains(\"<!DOCTYPE\")))\n}","tryCatchPattern":"match resource.text().await {\n    Err(e) if e.to_string().contains(\"Got HTML instead of text\") => {\n        eprintln!(\"check the URL serves raw text and no proxy intercepts it\");\n        std::process::exit(1);\n    }\n    other => other?,\n}","preventionTips":["Point mirrors at endpoints that serve raw text/artifacts, not HTML pages.","Configure NO_PROXY or authenticate to captive portals before running mise.","Test custom URLs with `curl -sL <url> | head -1` — expect non-HTML output."],"tags":["network","http","html","proxy"],"backgroundTag":"invalid-json-response","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"}