jdx/mise · error
Got HTML instead of text from {url}
Error message
Got HTML instead of text from {url} What it means
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.
Source
Thrown at src/http.rs:1560
.client
.send_with_https_fallback_with_retries(
Method::GET,
url.clone(),
&headers,
"GET",
self.retries,
true,
)
.await?;
let text = resp.text().await?;
if text.starts_with("<!DOCTYPE html>") {
if url.scheme() == "http" {
// try with https since http may be blocked
url.set_scheme("https").unwrap();
self.url = Ok(url);
return Box::pin(self.send()).await;
}
bail!("Got HTML instead of text from {}", url);
}
Ok(text)
}
}
fn is_github_forbidden(url: &Url, resp: &Response) -> bool {
resp.status() == StatusCode::FORBIDDEN && url.host_str() == Some("api.github.com")
}
fn is_github_unauthorized(url: &Url, resp: &Response) -> bool {
resp.status() == StatusCode::UNAUTHORIZED && crate::github::is_github_api_url(url)
}
/// Maximum body bytes buffered when building a GitHub error report, so an
/// oversized or slow-trickling error response can't exhaust memory. The overall
/// request timeout bounds the time; this bounds the memory.
const MAX_ERROR_BODY_BYTES: usize = 64 * 1024;
View on GitHub (pinned to afd2eddd3a)
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.
Example fix
// before MISE_NODE_MIRROR=https://github.com/nodejs/node/releases/download // after: point at an endpoint serving plain text/real artifacts MISE_NODE_MIRROR=https://nodejs.org/dist
Defensive patterns
Strategy: validation
Validate before calling
let body = client.get(url).send().await?.text().await?;
let looks_html = body.trim_start().starts_with('<')
&& (body.contains("<html") || body.contains("<!DOCTYPE html"));
if looks_html { return Err("endpoint returned HTML; check URL/proxy".into()); } Type guard
fn is_html(body: &str) -> bool {
let t = body.trim_start();
(t.starts_with('<') && (t.contains("<html") || t.contains("<!DOCTYPE")))
} Try / catch
match resource.text().await {
Err(e) if e.to_string().contains("Got HTML instead of text") => {
eprintln!("check the URL serves raw text and no proxy intercepts it");
std::process::exit(1);
}
other => other?,
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — this error's family across 28 libraries.
Related errors
- Got HTML instead of text from {}
- cannot encode #{value.class} as JSON
- Failed to read schema at {}: {}
- Failed to parse schema JSON: {}
- cli feature is not enabled
AI-assisted analysis of jdx/mise@afd2eddd3a (2026-09-09).
Data as JSON: /api/errors/dddbb7c3298a9f5f.
Report an issue: GitHub.