{"record":{"id":"60305c6676125bef","repo":"pypa/pip","slug":"invalid-sdist-filename-empty-project-name-file","errorCode":null,"errorMessage":"Invalid sdist filename (empty project name): {filename!r}","messagePattern":"Invalid sdist filename \\(empty project name\\): (.+?)","errorType":"validation","errorClass":"InvalidSdistFilename","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/utils.py","lineNumber":357,"sourceCode":"    .. _Source distribution format: https://packaging.python.org/specifications/source-distribution-format/#source-distribution-file-name\n    \"\"\"\n    if filename.endswith(\".tar.gz\"):\n        file_stem = filename[: -len(\".tar.gz\")]\n    elif filename.endswith(\".zip\"):\n        file_stem = filename[: -len(\".zip\")]\n    else:\n        raise InvalidSdistFilename(\n            f\"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):\"\n            f\" {filename!r}\"\n        )\n\n    # We are requiring a PEP 440 version, which cannot contain dashes,\n    # so we split on the last dash.\n    name_part, sep, version_part = file_stem.rpartition(\"-\")\n    if not sep:\n        raise InvalidSdistFilename(f\"Invalid sdist filename: {filename!r}\")\n    if not name_part:\n        raise InvalidSdistFilename(\n            f\"Invalid sdist filename (empty project name): {filename!r}\"\n        )\n\n    name = canonicalize_name(name_part)\n\n    try:\n        version = Version(version_part)\n    except InvalidVersion as e:\n        raise InvalidSdistFilename(\n            f\"Invalid sdist filename (invalid version): {filename!r}\"\n        ) from e\n\n    return (name, version)\n","sourceCodeStart":339,"sourceCodeEnd":371,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/utils.py#L339-L371","documentation":"Raised by parse_sdist_filename() when the project-name portion (everything before the last dash) is empty (utils.py:356-359). This happens when the stem starts with a dash, e.g. '-1.0', so rpartition('-') returns ('', '-', '1.0') with an empty name_part.","triggerScenarios":"A filename whose stem begins with a dash, e.g. parse_sdist_filename('-1.0.tar.gz') where the name part before the last dash is empty.","commonSituations":"A malformed filename produced by a buggy build tool, or a manually constructed filename where the project name was accidentally omitted.","solutions":["Ensure the project name is non-empty and appears before the version in the filename.","Rename the file to include a valid project name: {name}-{version}.tar.gz.","Validate filenames from external sources before parsing."],"exampleFix":"// before\nparse_sdist_filename('-1.0.tar.gz')\n// after\nparse_sdist_filename('foo-1.0.tar.gz')","handlingStrategy":"validation","validationCode":"def safe_parse_sdist(filename: str):\n    stem = filename[:-7] if filename.endswith(\".tar.gz\") else filename[:-4]\n    name_part, sep, version_part = stem.rpartition(\"-\")\n    if not name_part:\n        raise ValueError(f\"Sdist filename has empty project name: {filename!r}\")\n    return parse_sdist_filename(filename)","typeGuard":"def has_nonempty_sdist_name(filename: str) -> bool:\n    if not isinstance(filename, str):\n        return False\n    if filename.endswith(\".tar.gz\"):\n        stem = filename[:-7]\n    elif filename.endswith(\".zip\"):\n        stem = filename[:-4]\n    else:\n        return False\n    name_part, sep, _ = stem.rpartition(\"-\")\n    return bool(sep) and bool(name_part)","tryCatchPattern":"from packaging.utils import InvalidSdistFilename\n\ntry:\n    name, ver = parse_sdist_filename(filename)\nexcept InvalidSdistFilename as e:\n    if \"empty project name\" in str(e):\n        logger.error(\"Sdist filename starts with a dash: %s\", filename)\n    raise","preventionTips":["Ensure the project name is non-empty and appears before the version","Never start an sdist filename with a dash","Validate the name segment is present after rpartition('-')"],"tags":["sdist","filename","project-name","packaging"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}