jdx/mise · error

invalid Hex OTP checksum for {release_tag}: {checksum}

Error message

invalid Hex OTP checksum for {release_tag}: {checksum}

What it means

The matched builds.txt line's 4th whitespace-separated field must be a 64-character lowercase hex sha256. This error fires when the field exists but fails that format — realistically a corrupted or hijacked builds.txt: a proxy/captive portal returning an HTML page that got stored by mise's text cache, a truncated download, or an upstream hex.pm format change. Fatal with erlang.compile=false, otherwise falls back to source.

Source

Thrown at src/plugins/core/erlang.rs:815

        .lines()
        .filter_map(|line| {
            let parts = line.split_whitespace().collect::<Vec<_>>();
            (parts.first() == Some(&release_tag)).then_some(parts)
        })
        .collect::<Vec<_>>();

    if matching.len() != 1 {
        bail!(
            "expected exactly one Hex OTP build record for {release_tag}, found {}",
            matching.len()
        );
    }

    let checksum = matching[0]
        .get(3)
        .ok_or_else(|| eyre::eyre!("Hex OTP build record for {release_tag} has no checksum"))?;
    if !regex!(r"^[0-9a-f]{64}$").is_match(checksum) {
        bail!("invalid Hex OTP checksum for {release_tag}: {checksum}");
    }

    Ok(format!("sha256:{checksum}"))
}

#[cfg(any(linux, test))]
fn require_locked_precompiled_checksum(version: &str, checksum: Option<&str>) -> Result<String> {
    let checksum = checksum.ok_or_else(|| {
        eyre::eyre!(
            "No lockfile checksum found for precompiled Erlang/OTP {version}; regenerate mise.lock with `mise lock`"
        )
    })?;
    if !regex!(r"^sha256:[0-9a-f]{64}$").is_match(checksum) {
        bail!(
            "Invalid lockfile checksum for precompiled Erlang/OTP {version}; regenerate mise.lock with `mise lock`"
        );
    }

View on GitHub (pinned to 6f52dcdf99)

Solutions

  1. Inspect the live file: `curl -sSL https://builds.hex.pm/builds/otp/amd64/ubuntu-24.04/builds.txt | head -3` — if it is HTML, fix the network/proxy
  2. Clear mise's HTTP cache and retry: `mise cache` (or remove the cache dir shown by `mise cache dir`)
  3. Bypass TLS interception for builds.hex.pm in the proxy profile
  4. Workaround: `mise settings set erlang.compile true` to skip the builds.txt path entirely

Example fix

# before
$ mise install erlang@27.1
ERROR: invalid Hex OTP checksum for OTP-27.1: <html>...

# after
$ curl -sSL https://builds.hex.pm/builds/otp/amd64/ubuntu-24.04/builds.txt | head -1   # confirm real data
$ mise cache   # drop poisoned cache
$ mise install erlang@27.1
Defensive patterns

Strategy: retry

Validate before calling

# sanity-check the live builds.txt payload before relying on it
curl -fsSL https://builds.hex.pm/builds/otp/amd64/ubuntu-24.04/builds.txt \
  | grep -E '^OTP-[^ ]+ .+ [0-9a-f]{64}$' >/dev/null \
  || { echo 'builds.txt malformed (proxy/CDN issue?)'; exit 1; }

Try / catch

if ! mise install erlang@27.1; then
  if mise install erlang@27.1 2>&1 | grep -q 'invalid Hex OTP checksum'; then
    mise cache   # drop poisoned text cache, then retry once
    mise install erlang@27.1
  fi
fi

Prevention

When it happens

Trigger: `mise install erlang` (compile=false) behind a corporate TLS-intercepting proxy that serves an error page for builds.hex.pm; a stale cached builds.txt from a partial fetch; upstream temporarily serving a malformed file.

Common situations: Corporate networks with SSL inspection; hotel/airport captive portals poisoning caches; CDN incidents serving error pages with 200 status.

Related errors


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