{"record":{"id":"43a7092b7b4ca4ea","repo":"headroomlabs-ai/headroom","slug":"sha256-mismatch-for-path-name-expected-expecte","errorCode":null,"errorMessage":"sha256 mismatch for {path.name}: expected {expected}, got {got}","messagePattern":"sha256 mismatch for (.+?): expected (.+?), got (.+?)","errorType":"exception","errorClass":"Sha256Mismatch","httpStatus":null,"severity":"error","filePath":"headroom/binaries.py","lineNumber":319,"sourceCode":"def _sha256_file(path: Path) -> str:\n    h = hashlib.sha256()\n    with path.open(\"rb\") as f:\n        for chunk in iter(lambda: f.read(1024 * 64), b\"\"):\n            h.update(chunk)\n    return h.hexdigest()\n\n\ndef _verify_sha256(path: Path, expected: str | None) -> None:\n    if not expected:\n        # Upstream release not SHA-pinned in registry. HTTPS + the GitHub CDN\n        # is the only integrity check. Log at INFO so verbose runs can see\n        # this state; `doctor` surfaces the same fact via `sha_pinned=False`.\n        logger.info(\"binary %s downloaded without sha256 pin (HTTPS trust only)\", path.name)\n        return\n    got = _sha256_file(path)\n    if got.lower() != expected.lower():\n        path.unlink(missing_ok=True)\n        raise Sha256Mismatch(f\"sha256 mismatch for {path.name}: expected {expected}, got {got}\")\n\n\n# ---------- Archive extraction ------------------------------------------- #\n\n\ndef _extract(archive: Path, member: str, dest: Path) -> None:\n    \"\"\"Extract `member` from archive into `dest` (single-file binary).\"\"\"\n    if not _has_writable_existing_parent(dest.parent):\n        raise OSError(f\"binary cache directory parent is not writable: {dest.parent}\")\n    dest.parent.mkdir(parents=True, exist_ok=True)\n    if not _is_writable_dir(dest.parent):\n        raise OSError(f\"binary cache directory is not writable: {dest.parent}\")\n    name = archive.name.lower()\n    try:\n        if name.endswith(\".tar.gz\") or name.endswith(\".tgz\"):\n            with tarfile.open(archive, \"r:gz\") as tf:\n                _extract_member_from_tar(tf, member, dest)\n        elif name.endswith(\".zip\"):","sourceCodeStart":301,"sourceCodeEnd":337,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/binaries.py#L301-L337","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"# before\nHEADROOM_BINARIES_MIRROR=https://mirror.internal/gh\n# -> sha256 mismatch: expected a1b2..., got c3d4...\n\n# after\n# verify mirror integrity, or bypass it for this fetch:\nHEADROOM_BINARIES_MIRROR= headroom doctor  # re-download from github.com","handlingStrategy":"try-catch","validationCode":"import hashlib\n\ndef artifact_matches_pin(url: str, expected_sha: str | None) -> bool:\n    if not expected_sha:\n        return True  # unpinned: HTTPS-only trust\n    data = urllib.request.urlopen(url, timeout=60).read()\n    return hashlib.sha256(data).hexdigest() == expected_sha.lower()\n\n# pre-check mirror integrity before enabling it fleet-wide","typeGuard":null,"tryCatchPattern":"from headroom.binaries import Sha256Mismatch\n\ntry:\n    ensure_binary(tool)\nexcept Sha256Mismatch as e:\n    # do NOT retry blindly: decide which side is wrong\n    logger.critical(\"integrity failure: %s\", e)\n    raise SystemExit(\"mirror or registry is serving/pinning wrong bytes; escalate\") from e","preventionTips":["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."],"tags":["python","binaries","integrity","sha256","security","mirror"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}