{"record":{"id":"901eaa0c6db66724","repo":"nodejs/node","slug":"invalid-wheel-filename-invalid-version-filenam","errorCode":null,"errorMessage":"Invalid wheel filename (invalid version): {filename}","messagePattern":"Invalid wheel filename \\(invalid version\\): (.+?)","errorType":"validation","errorClass":"InvalidWheelFilename","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/packaging/utils.py","lineNumber":128,"sourceCode":"\n    filename = filename[:-4]\n    dashes = filename.count(\"-\")\n    if dashes not in (4, 5):\n        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (wrong number of parts): {filename}\"\n        )\n\n    parts = filename.split(\"-\", dashes - 2)\n    name_part = parts[0]\n    # See PEP 427 for the rules on escaping the project name.\n    if \"__\" in name_part or re.match(r\"^[\\w\\d._]*$\", name_part, re.UNICODE) is None:\n        raise InvalidWheelFilename(f\"Invalid project name: {filename}\")\n    name = canonicalize_name(name_part)\n\n    try:\n        version = Version(parts[1])\n    except InvalidVersion as e:\n        raise InvalidWheelFilename(\n            f\"Invalid wheel filename (invalid version): {filename}\"\n        ) from e\n\n    if dashes == 5:\n        build_part = parts[2]\n        build_match = _build_tag_regex.match(build_part)\n        if build_match is None:\n            raise InvalidWheelFilename(\n                f\"Invalid build number: {build_part} in '{filename}'\"\n            )\n        build = cast(BuildTag, (int(build_match.group(1)), build_match.group(2)))\n    else:\n        build = ()\n    tags = parse_tag(parts[-1])\n    return (name, version, build, tags)\n\n\ndef parse_sdist_filename(filename: str) -> Tuple[NormalizedName, Version]:","sourceCodeStart":110,"sourceCodeEnd":146,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/packaging/utils.py#L110-L146","documentation":"InvalidWheelFilename raised when the version segment (parts[1]) of the wheel filename fails to parse as a PEP 440 Version. The inner InvalidVersion is chained via 'from e'. The version field is mandatory in PEP 427 and must be a valid PEP 440 version.","triggerScenarios":"A wheel whose version field is a non-PEP-440 string such as 'latest', '1.0.0-build.7' with disallowed syntax, or a date format not expressible in PEP 440.","commonSituations":"Custom version schemes that do not conform to PEP 440, or filenames where the version field was accidentally truncated/reordered during manual renaming.","solutions":["Ensure the package version is a valid PEP 440 string (e.g. '1.0.0', '1.0.0a1', '1.0.0+local').","Let the build backend derive the version rather than hardcoding an ad-hoc string.","Inspect the chained InvalidVersion __cause__ for the precise PEP 440 violation."],"exampleFix":"# before\n# 'mypkg-1.0.BUILD7-py3-none-any.whl'\n\n# after\n# 'mypkg-1.0.0+build7-py3-none-any.whl'","handlingStrategy":"validation","validationCode":"from packaging.version import Version, InvalidVersion\ndef wheel_version_is_valid(filename: str) -> bool:\n    ver_part = filename[:-4].split('-')[1]\n    try:\n        Version(ver_part)\n        return True\n    except InvalidVersion:\n        return False","typeGuard":null,"tryCatchPattern":"try:\n    parse_wheel_filename(fn)\nexcept InvalidWheelFilename as e:\n    cause = e.__cause__  # InvalidVersion with details\n    # fix version in project metadata, rebuild","preventionTips":["Use PEP 440 versions in project metadata.","Let the backend derive versions from a single source of truth.","Validate versions before release via twine check."],"tags":["packaging","utils","wheel","version","pep440","pep427"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}