dotnet/aspnetcore · error · ValueError

Invalid Debian version format

Error message

Invalid Debian version format: {version}

What it means

Raised as a ValueError by parse_debian_version when a version string does not match the regex ^(?:(\d+):)?([^-]+)(?:-(.+))?$. The regex is permissive (epoch optional, upstream part is any non-dash run, revision optional), so a failure means the version is empty, starts with '-', or contains a character class the regex does not accept for the upstream segment.

Solutions

  1. Inspect the Packages.gz content for the package named in the error context — `apt-cache showsrc <pkg>` against the same mirror, or gunzip and grep the local index for the Version line.
  2. If the mirror has a malformed entry, switch --mirror or --suite to one with a well-formed index.
  3. Pre-filter parsed packages in parse_package_index to skip entries whose Version does not match the regex, so one bad row does not abort the whole run.
  4. If you control the input, normalize version strings upstream to the canonical Debian form epoch:upstream-revision.

Example fix

# before: malformed Version in index breaks parse_debian_version
# (e.g. Version: -1 in a custom Packages file)

# after: guard in parse_package_index before inserting
version = fields.get("Version")
if not version or not re.match(r'^(?:(\d+):)?([^-]+)(?:-(.+))?$', version):
    print(f"Skipping {package_name}: invalid Version '{version}'")
    continue
Defensive patterns

Strategy: validation

Validate before calling

# Before the comparison step, scan the parsed index for malformed Version fields:
gunzip -c "$MIRROR/dists/$SUITE/main/binary-$ARCH/Packages.gz" | awk 'BEGIN{RS=""}/^Version: [^:A-Za-z0-9.+~-]/||/^Version: $/{print "bad:",$0}'

Type guard

import re
VALID_VERSION = re.compile(r'^(?:(\d+):)?([^-]+)(?:-(.+))?$')
def is_valid_debian_version(v: str | None) -> bool:
    return bool(v) and bool(VALID_VERSION.match(v))

Prevention

When it happens

Trigger: compare_debian_versions calls parse_debian_version on both arguments; parse_package_index reads Version from Packages entries and then compares them via compare_debian_versions. A malformed Version field in the Packages index (or a None passed through) reaches the regex and fails to match.

Common situations: Packages entry has an empty Version field or one with an unusual epoch like ':' with no digits; a hand-edited or partially-local Packages index; native vs non-native versioning edge case where upstream contains characters the regex's [^-]+ still allows but the surrounding groups trip on; None reaching the function because parse_package_index's regex didn't capture Version.

Related errors


AI-assisted analysis of dotnet/aspnetcore@3600ca084e (2026-08-11). Data as JSON: /api/errors/ea7261baa6c9cec3. Report an issue: GitHub.

Appendix: source

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

def parse_release_file(content, path):
    """Parses the Release file and returns sha256 checksum of the specified path."""

    # data looks like this:
    # <checksum>  <size>  <path>
    matches = re.findall(r'^ (\S*) +(\S*) +(\S*)$', content, re.MULTILINE)

    for entry in matches:
        # the file has both md5 and sha256 checksums, we want sha256 which has a length of 64
        if entry[2] == path and len(entry[0]) == 64:
            return entry[0]

    raise Exception(f"Could not find checksum for {path} in Release file.")

def parse_debian_version(version):
    """Parse a Debian package version into epoch, upstream version, and revision."""
    match = re.match(r'^(?:(\d+):)?([^-]+)(?:-(.+))?$', version)
    if not match:
        raise ValueError(f"Invalid Debian version format: {version}")
    epoch, upstream, revision = match.groups()
    return int(epoch) if epoch else 0, upstream, revision or ""

def compare_upstream_version(v1, v2):
    """Compare upstream or revision parts using Debian rules."""
    def tokenize(version):
        tokens = re.split(r'([0-9]+|[A-Za-z]+)', version)
        return [int(x) if x.isdigit() else x for x in tokens if x]

    tokens1 = tokenize(v1)
    tokens2 = tokenize(v2)

    for token1, token2 in zip(tokens1, tokens2):
        if type(token1) == type(token2):
            if token1 != token2:
                return (token1 > token2) - (token1 < token2)
        else:
            return -1 if isinstance(token1, str) else 1

View on GitHub (pinned to 3600ca084e)