pypa/pip · error · InvalidSdistFilename

Invalid sdist filename (extension must be '.tar.gz' or '.zip

Error message

Invalid sdist filename (extension must be '.tar.gz' or '.zip'): {filename!r}

What it means

Raised by `parse_sdist_filename(filename)` when the filename ends with neither `.tar.gz` nor `.zip`. These are the only two legal sdist archive extensions per the source-distribution format; the parser checks the extension first before extracting the stem.

Source

Thrown at src/pip/_vendor/packaging/utils.py:276

        with an sdist extension (``.zip`` or ``.tar.gz``), or if it does not
        contain a dash separating the name and the version of the distribution.

    >>> from packaging.utils import parse_sdist_filename
    >>> from packaging.version import Version
    >>> name, ver = parse_sdist_filename("foo-1.0.tar.gz")
    >>> name
    'foo'
    >>> ver == Version('1.0')
    True

    .. _Source distribution format: https://packaging.python.org/specifications/source-distribution-format/#source-distribution-file-name
    """
    if filename.endswith(".tar.gz"):
        file_stem = filename[: -len(".tar.gz")]
    elif filename.endswith(".zip"):
        file_stem = filename[: -len(".zip")]
    else:
        raise InvalidSdistFilename(
            f"Invalid sdist filename (extension must be '.tar.gz' or '.zip'):"
            f" {filename!r}"
        )

    # We are requiring a PEP 440 version, which cannot contain dashes,
    # so we split on the last dash.
    name_part, sep, version_part = file_stem.rpartition("-")
    if not sep:
        raise InvalidSdistFilename(f"Invalid sdist filename: {filename!r}")

    name = canonicalize_name(name_part)

    try:
        version = Version(version_part)
    except InvalidVersion as e:
        raise InvalidSdistFilename(
            f"Invalid sdist filename (invalid version): {filename!r}"
        ) from e

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Route by extension: call `parse_sdist_filename` only for `.tar.gz`/`.zip`.
  2. Re-download or re-package the sdist into a supported archive format.
  3. Pre-check `filename.endswith(('.tar.gz', '.zip'))` before parsing.
  4. For `.tgz`, first normalize/rename to `.tar.gz`.

Example fix

// before
parse_sdist_filename('foo-1.0.whl')  # raises

// after
if filename.endswith(('.tar.gz', '.zip')):
    name, ver = parse_sdist_filename(filename)
elif filename.endswith('.whl'):
    name, ver, build, tags = parse_wheel_filename(filename)
else:
    raise ValueError(f"unsupported archive: {filename!r}")
Defensive patterns

Strategy: validation

Validate before calling

def is_sdist_filename(filename: str) -> bool:
    return filename.endswith(('.tar.gz', '.zip'))

Type guard

def is_sdist_filename(name: object) -> bool:
    return isinstance(name, str) and name.endswith(('.tar.gz', '.zip'))

Try / catch

from pip._vendor.packaging.utils import parse_sdist_filename, InvalidSdistFilename

if filename.endswith(('.tar.gz', '.zip')):
    try:
        name, ver = parse_sdist_filename(filename)
    except InvalidSdistFilename:
        skip(filename)
else:
    # route to wheel parser or skip
    pass

Prevention

When it happens

Trigger: Passing a `.whl`, `.egg`, `.tar.bz2`, `.tgz`, or any extension other than `.tar.gz`/`.zip` to `parse_sdist_filename`.

Common situations: Dispatching all archive names through the sdist parser; a directory of mixed wheels/sdists; an artifact renamed without preserving the `.tar.gz`/`.zip` extension; legacy `.tgz` archives.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/4420e19b25be45b0.json. Report an issue: GitHub.