{"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":"validation","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/3a2006ef569de08368d59db5e1468aa8f407e4f8/eng/common/cross/install-debs.py#L142-L178","documentation":"Raised by parse_debian_version (a ValueError) when the version string does not match '^(?:(\\d+):)?([^-]+)(?:-(.+))$'. The regex requires a non-empty upstream part that contains no '-', and a numeric epoch. It is stricter than Debian policy, so it can reject real-world versions or, more commonly, a None/empty Version field from a malformed Packages index.","triggerScenarios":"compare_debian_versions is called (during the 'keep highest version' step in parse_package_index, line 238) with a version string the regex rejects. Most often because Version is None (field missing in the entry) or empty.","commonSituations":"A Packages index entry missing its Version field, a truncated/partial index, a corrupt mirror, or an exotic version (epoch not numeric, upstream containing only dashes) the regex refuses.","solutions":["Identify which package/version triggers it: log the version string before parse_debian_version is called.","Inspect the Packages index for entries with a missing or malformed Version field and exclude the offending package.","Re-download the index in case it was truncated.","Loosen the parser: guard against None/empty and accept dashes in the upstream portion per Debian policy."],"exampleFix":"// before\ndef parse_debian_version(version):\n    match = re.match(r'^(?:(\\d+):)?([^-]+)(?:-(.+))?$', version)\n    if not match:\n        raise ValueError(f\"Invalid Debian version format: {version}\")\n\n// after\ndef parse_debian_version(version):\n    if not isinstance(version, str) or not version:\n        raise ValueError(f\"Invalid Debian version format: {version!r}\")\n    match = re.match(r'^(?:(\\d+):)?(.+)$', version)  # accept any non-empty body, split revision later\n    if not match:\n        raise ValueError(f\"Invalid Debian version format: {version!r}\")","handlingStrategy":"validation","validationCode":"# preflight: drop index entries with missing/blank Version before version comparison\npackages, aliases = parse_package_index(content)\nbad = [name for name, info in packages.items() if not info.get(\"Version\")]\nif bad:\n    sys.exit(f\"Packages index has entries without Version (will crash version compare): {bad}\")","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Filter out Packages entries missing Version/Filename/SHA256 right after parse_package_index.","Re-download the index if it looks truncated (unparseable entries).","Prefer a Debian-policy-compliant version parser over the strict regex if you hit exotic versions."],"tags":["parsing","debian-version","validation"],"backgroundTag":null,"analyzedSha":"3a2006ef569de08368d59db5e1468aa8f407e4f8","analyzedAt":"2026-08-11T23:42:04.146Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}