{"id":"dda11911bd4e24b7","repo":"python-poetry/poetry","slug":"unable-to-parse-build-tag-wheel-build-tag","errorCode":null,"errorMessage":"Unable to parse build tag: {wheel.build_tag}","messagePattern":"Unable to parse build tag: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/poetry/installation/chooser.py","lineNumber":309,"sourceCode":"              comparison operators, but then different sdist links\n              with the same version, would have to be considered equal\n        \"\"\"\n        build_tag: tuple[Any, ...] = ()\n        binary_preference = 0\n        if link.is_wheel:\n            wheel = Wheel(link.filename)\n            if not wheel.is_supported_by_environment(self._env):\n                raise RuntimeError(\n                    f\"{wheel.filename} is not a supported wheel for this platform. It \"\n                    \"can't be sorted.\"\n                )\n\n            # TODO: Binary preference\n            pri = -(wheel.get_minimum_supported_index(self._env.supported_tags) or 0)\n            if wheel.build_tag is not None:\n                match = re.match(r\"^(\\d+)(.*)$\", wheel.build_tag)\n                if not match:\n                    raise ValueError(f\"Unable to parse build tag: {wheel.build_tag}\")\n                build_tag_groups = match.groups()\n                build_tag = (int(build_tag_groups[0]), build_tag_groups[1])\n        else:  # sdist\n            support_num = len(self._env.supported_tags)\n            pri = -support_num\n\n        has_allowed_hash = int(self._is_link_hash_allowed_for_package(link, package))\n\n        yank_value = int(not link.yanked)\n\n        return (\n            has_allowed_hash,\n            yank_value,\n            binary_preference,\n            package.version,\n            build_tag,\n            pri,\n        )","sourceCodeStart":291,"sourceCodeEnd":327,"githubUrl":"https://github.com/python-poetry/poetry/blob/92b74dcfe348d0e01e14d40d6c1fa47a4ee04a54/src/poetry/installation/chooser.py#L291-L327","documentation":"Raised by Chooser._sort_key at src/poetry/installation/chooser.py:307-309 when a wheel's build_tag is present but does not match the regex `^(\\d+)(.*)$`. Build tags must be an integer followed by an optional label (PEP 427); anything else is a malformed filename. ValueError.","triggerScenarios":"A wheel filename like `pkg-1.0-abc-py3-none-any.whl` where the build tag segment is non-numeric. Wheel(link.filename).build_tag is non-None (so the segment exists) but the leading characters are not digits.","commonSituations":"A private index hosting a hand-renamed wheel, a packaging tool emitting non-standard build tags, or a filename collision that looks like a build tag.","solutions":["Inspect the wheel filename and confirm the build-tag segment (after version, before python tag) is digits-only optionally followed by a label.","Rebuild/rename the wheel to comply with PEP 427 (e.g. `pkg-1.0-1-py3-none-any.whl`).","Remove the malformed wheel from the index if it is not yours to fix."],"exampleFix":"# before (malformed)\npkg-1.0-abc-py3-none-any.whl\n\n# after\npkg-1.0-1-py3-none-any.whl","handlingStrategy":"validation","validationCode":"import re\n\ndef valid_build_tag(build_tag: str | None) -> bool:\n    return build_tag is None or bool(re.match(r'^(\\d+)(.*)$', build_tag))","typeGuard":null,"tryCatchPattern":"try:\n    sorted(candidates, key=chooser._sort_key)\nexcept ValueError as e:\n    if 'Unable to parse build tag' in str(e):\n        # skip the malformed wheel from the candidate set\n        candidates = [c for c in candidates if not has_bad_build_tag(c.link)]\n        sorted(candidates, key=chooser._sort_key)\n    raise","preventionTips":["Validate wheel filenames against PEP 427 before hosting on private indexes.","Rename malformed wheels to use a numeric build tag."],"tags":["wheel","build-tag","pep427","validation","installation"],"analyzedSha":"92b74dcfe348d0e01e14d40d6c1fa47a4ee04a54","analyzedAt":"2026-08-04T20:33:34.072Z","schemaVersion":2}