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
- Verify the expected sha256 against the official upstream release notes/checksums file
- If the version was bumped, recompute the digest from the new artifact: `curl -sL <url> | shasum -a 256`
- Retry the download in case of transient network corruption
- 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
- Cross-check digests against official upstream checksum files
- Re-pin digests when bumping versions and note the source
- Retry transient downloads before assuming tampering
- Prefer immutable, versioned URLs over moving tags
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
- Checksum mismatch for file
- Size mismatch for : expected , got
- brew-cask: : cask metadata has no sha256
- # : malformed sha256
- # : missing sha256
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)