{"record":{"id":"fe2b4eaa343104d5","repo":"jdx/mise","slug":"got-html-instead-of-text-from","errorCode":null,"errorMessage":"Got HTML instead of text from {}","messagePattern":"Got HTML instead of text from (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/http.rs","lineNumber":1461,"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":1443,"sourceCodeEnd":1479,"githubUrl":"https://github.com/jdx/mise/blob/6f52dcdf99e282ef7a7db68c81301fa4618d0f79/src/http.rs#L1443-L1479","documentation":"After fetching text (usually JSON from an API) and applying retries, TextRequest::send checks whether the body starts with \"<!DOCTYPE html>\" (src/http.rs:1452-1463). APIs are expected to return text/JSON, so a leading DOCTYPE means an interstitial or block page came back. If the URL scheme was http, mise upgrades to https and retries once; otherwise it bails.","triggerScenarios":"A JSON/text endpoint returns HTML: captive portals on public Wi-Fi, SSL-inspection or DLP proxies substituting block pages, GitHub API rate-limit or interstitial pages, or a backend URL pointing at a website instead of the API — over plain http where even the https retry still returns HTML.","commonSituations":"Corporate laptops behind filtering proxies; hotel/airport Wi-Fi before login; a wrong URL (website path instead of API path) in a custom backend; heavy unauthenticated api.github.com use from shared CI IPs.","solutions":["Inspect what the URL returns in your environment: curl -sL <url> | head -c 300 — the HTML usually names the blocker (proxy, portal, rate limit).","Use https:// URLs in configs; mise only auto-upgrades http once.","Authenticate or allow-list the host on your proxy, or complete the captive-portal login.","For GitHub API URLs, export GITHUB_TOKEN / MISE_GITHUB_TOKEN and check rate limits and githubstatus.com."],"exampleFix":"# before\n$ mise use ubi:example/tool\nERROR Got HTML instead of text from https://api.example.com/releases\n\n# after — clear the network condition, then retry\n$ curl -sL https://api.example.com/releases | head -c 100   # identify the blocker (portal/proxy)\n$ export HTTPS_PROXY=http://proxy.corp:3128 && mise use ubi:example/tool","handlingStrategy":"validation","validationCode":"# CI preflight: endpoints must not return an HTML interstitial\ncurl -fsSL \"$url\" | head -c 15 | grep -q '<!DOCTYPE html>' && { echo \"blocked/HTML response from $url\"; exit 1; }","typeGuard":null,"tryCatchPattern":"On \"Got HTML instead of text\", stop and diagnose the environment (captive portal, proxy block page, rate limit) — retrying the same URL under the same network returns the same HTML. Re-attempt only after the condition changes: token set, portal cleared, proxy bypassed.","preventionTips":["Use https and authenticated API endpoints in configs.","Smoke-test critical URLs from the same network/CI environment before long runs.","Set MISE_GITHUB_TOKEN for anything touching api.github.com.","Check for captive portals on laptops before running mise."],"tags":["http","html","proxy","response-validation","network"],"backgroundTag":"html-instead-of-json","analyzedSha":"6f52dcdf99e282ef7a7db68c81301fa4618d0f79","analyzedAt":"2026-08-22T10:14:23.840Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}