{"record":{"id":"b8e239f0856cb36c","repo":"dotnet/efcore","slug":"invalid-debian-version-format-version","errorCode":null,"errorMessage":"Invalid Debian version format: {version}","messagePattern":"Invalid Debian version format: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"eng/common/cross/install-debs.py","lineNumber":160,"sourceCode":"def parse_release_file(content, path):\n    \"\"\"Parses the Release file and returns sha256 checksum of the specified path.\"\"\"\n\n    # data looks like this:\n    # <checksum>  <size>  <path>\n    matches = re.findall(r'^ (\\S*) +(\\S*) +(\\S*)$', content, re.MULTILINE)\n\n    for entry in matches:\n        # the file has both md5 and sha256 checksums, we want sha256 which has a length of 64\n        if entry[2] == path and len(entry[0]) == 64:\n            return entry[0]\n\n    raise Exception(f\"Could not find checksum for {path} in Release file.\")\n\ndef parse_debian_version(version):\n    \"\"\"Parse a Debian package version into epoch, upstream version, and revision.\"\"\"\n    match = re.match(r'^(?:(\\d+):)?([^-]+)(?:-(.+))?$', version)\n    if not match:\n        raise ValueError(f\"Invalid Debian version format: {version}\")\n    epoch, upstream, revision = match.groups()\n    return int(epoch) if epoch else 0, upstream, revision or \"\"\n\ndef compare_upstream_version(v1, v2):\n    \"\"\"Compare upstream or revision parts using Debian rules.\"\"\"\n    def tokenize(version):\n        tokens = re.split(r'([0-9]+|[A-Za-z]+)', version)\n        return [int(x) if x.isdigit() else x for x in tokens if x]\n\n    tokens1 = tokenize(v1)\n    tokens2 = tokenize(v2)\n\n    for token1, token2 in zip(tokens1, tokens2):\n        if type(token1) == type(token2):\n            if token1 != token2:\n                return (token1 > token2) - (token1 < token2)\n        else:\n            return -1 if isinstance(token1, str) else 1","sourceCodeStart":142,"sourceCodeEnd":178,"githubUrl":"https://github.com/dotnet/efcore/blob/dbf9771522148d61a2467854921bd5dc6f6e6916/eng/common/cross/install-debs.py#L142-L178","documentation":"Raised by parse_debian_version as a ValueError when the regex `^(?:(\\d+):)?([^-]+)(?:-(.+))$` fails to match the version string. The upstream group `[^-]+` requires at least one non-hyphen character, so this only fires for an empty version or one beginning with '-'. Invoked transitively via compare_debian_versions during dependency/index parsing.","triggerScenarios":"parse_debian_version(version) (or compare_debian_versions(v1, v2)) called with a malformed Version field - empty string, a string starting with '-', or a string consisting solely of hyphens. Reached when parse_package_index compares two package versions and one of them is malformed.","commonSituations":"Packages.gz with a corrupt/garbled Version field; a third-party or custom package using a non-conforming version; locale/encoding damage to the index; an empty Version field from a partially-written index entry.","solutions":["Inspect the Packages.gz content for the offending version: `gzip -dc Packages.gz | grep -B2 -A2 Version` and look for empty/garbled values.","Filter or skip the malformed package out of the desired package list / index before parsing.","If from a real distro index, report the bad package to the mirror/package maintainer - well-formed Debian versions always match.","Wrap compare_debian_versions in try/except ValueError to quarantine bad entries rather than aborting the whole run."],"exampleFix":"# before\nepoch, upstream, revision = parse_debian_version(version)\n# after (quarantine malformed versions instead of aborting)\ntry:\n    epoch, upstream, revision = parse_debian_version(version)\nexcept ValueError:\n    print(f\"Skipping package with malformed version: {version!r}\")\n    continue","handlingStrategy":"validation","validationCode":"# Validate a Debian version string before parse_debian_version sees it\nimport re\n\n_DEBIAN_VERSION_RE = re.compile(r'^(?:(\\d+):)?([^-]+)(?:-(.+))$')\n\ndef is_valid_debian_version(v):\n    return isinstance(v, str) and bool(_DEBIAN_VERSION_RE.match(v))\n\nfor name, info in packages_info.items():\n    if not is_valid_debian_version(info.get('Version')):\n        print(f\"Dropping {name}: bad Version {info.get('Version')!r}\"); packages_info.pop(name)","typeGuard":"def is_valid_debian_version(version):\n    \"\"\"True iff parse_debian_version(version) will not raise.\"\"\"\n    import re\n    return isinstance(version, str) and bool(\n        re.match(r'^(?:(\\d+):)?([^-]+)(?:-(.+))?$', version))","tryCatchPattern":"# compare_debian_versions is called transitively; quarantine bad entries\ntry:\n    newer = compare_debian_versions(version, existing_version) > 0\nexcept ValueError as e:\n    if \"Invalid Debian version format\" in str(e):\n        print(f\"Skipping malformed version pair: {version!r} vs {existing_version!r}\")\n        continue\n    raise","preventionTips":["Validate Version fields right after parse_package_index, before dependency resolution.","Treat a malformed Debian version as bad upstream data, not a crash - skip and log it.","Unit-test parse_debian_version against the distro's real version strings when adopting a new suite."],"tags":["parsing","validation","version","debian"],"analyzedSha":"dbf9771522148d61a2467854921bd5dc6f6e6916","analyzedAt":"2026-08-06T20:46:03.226Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}