jdx/mise · critical · RuntimeError

# : sha256 mismatch (expected # , got # )

Error message

#{context}: sha256 mismatch (expected #{sha256}, got #{actual})

What it means

A Ruby raise in the brew shim's fetch: the downloaded archive's SHA-256 does not match the expected digest. The temp file is deleted and the raise aborts the fetch, protecting against corrupted or tampered downloads.

Solutions

  1. Verify the expected sha256 against the official upstream release notes/checksums file
  2. If the version was bumped, recompute the digest from the new artifact: `curl -sL <url> | shasum -a 256`
  3. Retry the download in case of transient network corruption
  4. If upstream legitimately changed the artifact, update the pinned digest and record why

Example fix

# before
sha256 "b1946ac92492d2347c6235b4d2611184e0b46a1c"  # stale/other version
# after
sha256 "e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855"  # matches v1.2.3 tarball
Defensive patterns

Strategy: retry

Validate before calling

expected = Digest::SHA256.file(local_reference).hexdigest
raise "digest drift for #{context}" unless expected == pinned_sha256

Try / catch

begin
  retries ||= 0
  MiseDownload.fetch(url, sha256, context)
rescue RuntimeError => e
  raise if (retries += 1) > 2 || e.message.exclude?("mismatch")
  sleep 2
  retry
end

Prevention

When it happens

Trigger: The remote file at `url` changed upstream (re-tagged release), a mirror served a different artifact, network corruption produced a truncated file, or the package definition's sha256 belongs to a different version of the archive.

Common situations: Upstream force-pushes a release tag and re-uploads assets; CDN/proxy caching stale or wrong content; developers bumping a version URL without updating the digest.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of jdx/mise@533346cc37 (2026-09-17). Data as JSON: /api/errors/82240e9f4272fe5f. Report an issue: GitHub.

Appendix: source

Thrown at src/system/packages/brew/shim.rb:87

  # download with redirects into the cache, verify, return the path
  def fetch(url, sha256, context)
    raise "#{context}: missing sha256" if sha256.to_s.strip.empty?
    sha256 = sha256.to_s.strip.downcase
    raise "#{context}: malformed sha256" unless sha256.match?(/\A[0-9a-f]{64}\z/)

    MISE_BREW_CACHE.mkpath
    dest = MISE_BREW_CACHE + "#{sha256}--#{File.basename(URI(url).path)}"
    unless dest.file? && Digest::SHA256.file(dest).hexdigest == sha256
      ohai "Downloading #{url}"
      tmp = Pathname.new("#{dest}.incomplete")
      URI.open(url, "rb", redirect: true) do |remote|
        tmp.open("wb") { |f| IO.copy_stream(remote, f) }
      end
      actual = Digest::SHA256.file(tmp).hexdigest
      if actual != sha256
        tmp.unlink
        raise "#{context}: sha256 mismatch (expected #{sha256}, got #{actual})"
      end
      tmp.rename(dest)
    end
    dest
  end

  # unpack an archive the way brew stages sources: if the archive contains a
  # single top-level directory, its contents become the stage root
  def unpack(archive, dest)
    dest.mkpath
    case archive.basename.to_s
    when /\.(tar\.(gz|xz|bz2|zst)|tgz|txz|tbz2?|tar|crate)\z/i
      system_or_die "tar", "xf", archive.to_s, "-C", dest.to_s
    when /\.zip\z/i
      system_or_die "unzip", "-qo", archive.to_s, "-d", dest.to_s
    when /\.(gz|xz|bz2)\z/i
      data = `#{archive.to_s =~ /xz\z/ ? "xz -dc" : archive.to_s =~ /bz2\z/ ? "bzip2 -dc" : "gzip -dc"} #{Shellwords.escape(archive.to_s)}`
      raise "failed to decompress #{archive}" unless $?.success?

View on GitHub (pinned to 533346cc37)