dotnet/yarp · 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

This exception is raised in fetch_and_decompress when --force-check-gpg is enabled and the downloaded Packages.gz compressed bytes do not match the SHA-256 recorded for that path in the Release file. The Release file is fetched and parsed separately (fetch_release_file + parse_release_file), and its recorded checksum for the component/binary-arch/Packages.gz path is compared against the hash of the just-downloaded compressed data. A mismatch means the package index content on the mirror does not match what the signed Release file attests -- indicating mirror inconsistency, a stale Release file, or tampering.

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 bd11867bee)

Solutions

  1. Retry -- mirror sync inconsistencies are usually transient and resolve within minutes.
  2. Ensure both Packages.gz and Release are fetched from the same mirror node (avoid CDN-affinity issues by pinning to a specific mirror hostname).
  3. If the issue persists, disable --force-check-gpg temporarily (or use --skipsigcheck in build-rootfs.sh) to unblock, then investigate the mirror.
  4. Switch to a fully-synced primary mirror (e.g. deb.debian.org, archive.ubuntu.com) instead of a secondary/ports mirror.
  5. Verify the path string passed to parse_release_file exactly matches the path in the Release file (component name, binary-<arch> spelling).
  6. Use a timestamped snapshot (snapshot.debian.org) to guarantee Release and Packages.gz are from the same point in time.
Defensive patterns

Strategy: retry

Validate before calling

# Pre-check: verify the Release file contains the expected path before downloading Packages.gz.
release_content = await fetch_release_file(session, mirror, suite, keyring)
matches = re.findall(r'^(\S*) +(\S*) +(\S*)$', release_content, re.MULTILINE)
found = any(entry[2] == path and len(entry[0]) == 64 for entry in matches)
if not found:
    print(f"WARNING: {path} not found in Release file for suite {suite}.")

Try / catch

# Handle package-index checksum mismatch by retrying with a fresh fetch
try:
    content = await fetch_and_decompress(session, mirror, arch, suite, component, check_sig, keyring)
except Exception as e:
    if 'SHA256 mismatch' in str(e):
        print(f"Index checksum mismatch for {path}. Mirror may be mid-sync. Retrying...")
        await asyncio.sleep(10)
        content = await fetch_and_decompress(session, mirror, arch, suite, component, check_sig, keyring)
    else:
        raise

Prevention

When it happens

Trigger: fetch_and_decompress (lines 86-105) is called for each suite/component combination. When check_sig is True (passed via --force-check-gpg), after downloading Packages.gz it fetches the Release file, extracts the expected sha256 for that path, hashes the compressed bytes, and compares. The exception fires on mismatch. This happens when the mirror updated Packages.gz but not Release (or vice versa), when the Release file was fetched from a different mirror node than Packages.gz (CDN inconsistency), or when the path in the Release file does not correspond to the actual file served.

Common situations: Mirror CDN nodes are inconsistent (Packages.gz from one edge node, Release from another); the suite is mid-update and Release was published before the new Packages.gz (or after the old one was removed); using a Debian ports mirror with irregular sync cadence; the component/architecture path in the Release file doesn't exactly match what was downloaded (trailing slash, case sensitivity); a caching layer serves a stale Packages.gz while Release is fresh.

Related errors


AI-assisted analysis of dotnet/yarp@bd11867bee (2026-08-13). Data as JSON: /api/errors/ceb4220368f6a666. Report an issue: GitHub.