dotnet/efcore · critical · Exception

SHA256 mismatch for {path}: expected {packages_sha}, got {sh

Error message

SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}

What it means

Raised by fetch_and_decompress when --force-check-gpg is on: the SHA256 of the just-downloaded Packages.gz does not equal the SHA256 recorded for that path in the (signature-verified) Release file. A mismatch means the bytes served for the index are not the bytes the repository signed - either mirror/CDN inconsistency during a push, a truncated/corrupt download, or an active tampering attempt.

Source

Thrown at eng/common/cross/install-debs.py:105

    """Fetch and decompress the Packages.gz file."""

    path = f"{component}/binary-{arch}/Packages.gz"
    url = f"{mirror}/dists/{suite}/{path}"

    async with session.get(url) as response:
        if response.status == 200:
            compressed_data = await response.read()
            decompressed_data = gzip.decompress(compressed_data).decode('utf-8')
            print(f"Downloaded index: {url}")

            if check_sig:
                # Verify the package index against the sha256 recorded in the Release file
                release_file_content = await fetch_release_file(session, mirror, suite, keyring)
                packages_sha = parse_release_file(release_file_content, path)

                sha256 = hashlib.sha256(compressed_data).hexdigest()
                if sha256 != packages_sha:
                    raise Exception(f"SHA256 mismatch for {path}: expected {packages_sha}, got {sha256}")
                print(f"Checksum verified for {path}")

            return decompressed_data
        else:
            print(f"Skipped index: {url} (doesn't exist)")
            return None

async def fetch_release_file(session, mirror, suite, keyring):
    """Fetch Release and Release.gpg files and verify the signature."""

    release_url = f"{mirror}/dists/{suite}/Release"
    release_gpg_url = f"{mirror}/dists/{suite}/Release.gpg"

    with tempfile.NamedTemporaryFile() as release_file, tempfile.NamedTemporaryFile() as release_gpg_file:
        await download_file(session, release_url, release_file.name)
        await download_file(session, release_gpg_url, release_gpg_file.name)

        print("Verifying signature of Release with Release.gpg.")

View on GitHub (pinned to dbf9771522)

Solutions

  1. Retry once or twice - a transient mirror/CDN inconsistency usually resolves within minutes.
  2. Switch to a different mirror to rule out a single bad edge cache.
  3. Confirm the system clock is correct; verify the Release/Release.gpg URLs by hand.
  4. Only as a last resort for a known-good private mirror, drop --force-check-gpg (or pass --skipsigcheck to build-rootfs.sh) - never do this to silence a mismatch on a public mirror you do not control.
Defensive patterns

Strategy: try-catch

Validate before calling

# When --force-check-gpg is on, you cannot pre-verify without re-implementing the
# fetch; instead validate inputs: mirror is https, clock is synced, suite exists.
from datetime import datetime, timezone
import urllib.parse, ntplib  # 'pip install ntplib'

def preflight_for_checksum(mirror):
    if urllib.parse.urlparse(mirror).scheme != "https":
        raise RuntimeError("Use an https mirror when verifying checksums to avoid MITM")
    off = abs(ntplib.NTPClient().request('pool.ntp.org', version=3).tx_time - datetime.now(timezone.utc).timestamp())
    if off > 300:
        raise RuntimeError(f"System clock off by {off:.0f}s; fix NTP before checksum verification")

Try / catch

# A checksum mismatch is a security signal - never swallow it; surface and abort
try:
    await download_package_index_parallel(mirror, arch, suites, check_sig=True, keyring=keyring)
except Exception as e:
    if "SHA256 mismatch" in str(e):
        # try ONE alternate mirror; if it also mismatches, treat as a real integrity incident
        raise SystemExit(f"Integrity failure (do NOT ignore): {e}")
    raise

Prevention

When it happens

Trigger: fetch_and_decompress(..., check_sig=True) computes hashlib.sha256(compressed_data) and it differs from parse_release_file(release_file_content, path). Happens mid-mirror-update when Release and Packages.gz are briefly out of sync, when a CDN serves a stale/corrupt cached copy, or when a man-in-the-middle alters the payload.

Common situations: Hitting a mirror while it is mid-publish (Release updated before Packages.gz); CDN edge cache inconsistency; local transparent proxy rewriting/caching bodies; genuine MITM. Rare but security-meaningful.

Related errors


AI-assisted analysis of dotnet/efcore@dbf9771522 (2026-08-06). Data as JSON: /api/errors/d109f8cfda562305. Report an issue: GitHub.