jdx/mise · error

Got HTML instead of text from {}

Error message

Got HTML instead of text from {}

What it means

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.

Source

Thrown at src/http.rs:1461

            .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 6f52dcdf99)

Solutions

  1. Inspect what the URL returns in your environment: curl -sL <url> | head -c 300 — the HTML usually names the blocker (proxy, portal, rate limit).
  2. Use https:// URLs in configs; mise only auto-upgrades http once.
  3. Authenticate or allow-list the host on your proxy, or complete the captive-portal login.
  4. For GitHub API URLs, export GITHUB_TOKEN / MISE_GITHUB_TOKEN and check rate limits and githubstatus.com.

Example fix

# before
$ mise use ubi:example/tool
ERROR Got HTML instead of text from https://api.example.com/releases

# after — clear the network condition, then retry
$ curl -sL https://api.example.com/releases | head -c 100   # identify the blocker (portal/proxy)
$ export HTTPS_PROXY=http://proxy.corp:3128 && mise use ubi:example/tool
Defensive patterns

Strategy: validation

Validate before calling

# CI preflight: endpoints must not return an HTML interstitial
curl -fsSL "$url" | head -c 15 | grep -q '<!DOCTYPE html>' && { echo "blocked/HTML response from $url"; exit 1; }

Try / catch

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.

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of jdx/mise@6f52dcdf99 (2026-08-22). Data as JSON: /api/errors/fe2b4eaa343104d5. Report an issue: GitHub.