headroomlabs-ai/headroom · error · Sha256Mismatch
sha256 mismatch for {path.name}: expected {expected}, got {g
Error message
sha256 mismatch for {path.name}: expected {expected}, got {got} What it means
After download, _verify_sha256 hashes the file and compares (case-insensitively) against the registry-pinned sha256. On mismatch the downloaded file is deleted immediately (so a bad artifact never lingers in cache) and Sha256Mismatch is raised with both the expected and actual digests. When the registry has no pin, the function only logs at INFO and trusts HTTPS — this error therefore always means a pin WAS present and did not match.
Source
Thrown at headroom/binaries.py:319
def _sha256_file(path: Path) -> str:
h = hashlib.sha256()
with path.open("rb") as f:
for chunk in iter(lambda: f.read(1024 * 64), b""):
h.update(chunk)
return h.hexdigest()
def _verify_sha256(path: Path, expected: str | None) -> None:
if not expected:
# Upstream release not SHA-pinned in registry. HTTPS + the GitHub CDN
# is the only integrity check. Log at INFO so verbose runs can see
# this state; `doctor` surfaces the same fact via `sha_pinned=False`.
logger.info("binary %s downloaded without sha256 pin (HTTPS trust only)", path.name)
return
got = _sha256_file(path)
if got.lower() != expected.lower():
path.unlink(missing_ok=True)
raise Sha256Mismatch(f"sha256 mismatch for {path.name}: expected {expected}, got {got}")
# ---------- Archive extraction ------------------------------------------- #
def _extract(archive: Path, member: str, dest: Path) -> None:
"""Extract `member` from archive into `dest` (single-file binary)."""
if not _has_writable_existing_parent(dest.parent):
raise OSError(f"binary cache directory parent is not writable: {dest.parent}")
dest.parent.mkdir(parents=True, exist_ok=True)
if not _is_writable_dir(dest.parent):
raise OSError(f"binary cache directory is not writable: {dest.parent}")
name = archive.name.lower()
try:
if name.endswith(".tar.gz") or name.endswith(".tgz"):
with tarfile.open(archive, "r:gz") as tf:
_extract_member_from_tar(tf, member, dest)
elif name.endswith(".zip"):View on GitHub (pinned to 322425c43b)
Solutions
- Update headroom-ai so its registry carries the sha256 of the current upstream release.
- If using HEADROOM_BINARIES_MIRROR, verify the mirror serves byte-identical assets (compare sha256sum against GitHub's published digest).
- Independently confirm which side is wrong: download from github.com directly and hash it; if the direct hash matches 'got', the registry pin is stale (file an issue); if it matches 'expected', the mirror is tampering/corrupting.
- Treat unexpected mismatches on a trusted mirror as a security incident, not an inconvenience.
Example fix
# before HEADROOM_BINARIES_MIRROR=https://mirror.internal/gh # -> sha256 mismatch: expected a1b2..., got c3d4... # after # verify mirror integrity, or bypass it for this fetch: HEADROOM_BINARIES_MIRROR= headroom doctor # re-download from github.com
Defensive patterns
Strategy: try-catch
Validate before calling
import hashlib
def artifact_matches_pin(url: str, expected_sha: str | None) -> bool:
if not expected_sha:
return True # unpinned: HTTPS-only trust
data = urllib.request.urlopen(url, timeout=60).read()
return hashlib.sha256(data).hexdigest() == expected_sha.lower()
# pre-check mirror integrity before enabling it fleet-wide Try / catch
from headroom.binaries import Sha256Mismatch
try:
ensure_binary(tool)
except Sha256Mismatch as e:
# do NOT retry blindly: decide which side is wrong
logger.critical("integrity failure: %s", e)
raise SystemExit("mirror or registry is serving/pinning wrong bytes; escalate") from e Prevention
- Prefer sha256-pinned registry entries so integrity is enforced, not implied by HTTPS.
- Audit mirrors regularly: compare served digests against GitHub's published asset digests.
- Never disable verification to 'get past' a mismatch — investigate the divergence first.
When it happens
Trigger: The bytes served for the pinned URL differ from the registry's digest: upstream release re-tagged/overwritten without a registry update, a mirror serving a different (possibly malicious or truncated) artifact, or a corrupted download.
Common situations: Internal mirrors that re-pack assets, GitHub releases where a maintainer force-pushed a tag, stale headroom-ai registry after a tool upstream re-released, or genuine CDN corruption.
Related errors
- failed to download {final_url} after {attempts} attempts: {e
- {tool}: distributed via PyPI only; `pip install headroom-ai`
- {tool}: no prebuilt binary for {plat.key()}; supported: {sup
- offline mode (HEADROOM_BINARIES_OFFLINE=1) but fetch require
- binary cache directory parent is not writable: {dest.parent}
AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15).
Data as JSON: /api/errors/43a7092b7b4ca4ea.
Report an issue: GitHub.