python-poetry/poetry · error · FileNotFoundError

PKG-INFO not found in sdist

Error message

PKG-INFO not found in sdist

What it means

Raised by Uploader._get_metadata() when a .tar.gz sdist is opened but no top-level <name>-<version>/PKG-INFO member is found. PKG-INFO is the sdist's metadata file; its absence means the sdist is malformed. This blocks upload because metadata cannot be extracted.

Source

Thrown at src/poetry/publishing/uploader.py:364

                        len(parts) == 2
                        and parts[1] == "METADATA"
                        and parts[0].endswith(".dist-info")
                    ):
                        with z.open(name) as mf:
                            return parse_email(mf.read().decode("utf-8"))[0]
            raise FileNotFoundError("METADATA not found in wheel")

        elif file.suffixes[-2:] == [".tar", ".gz"]:
            with tarfile.open(file, "r:gz") as tar:
                for member in tar.getmembers():
                    parts = Path(member.name).parts
                    if (
                        len(parts) == 2
                        and parts[1] == "PKG-INFO"
                        and (pf := tar.extractfile(member))
                    ):
                        return parse_email(pf.read().decode("utf-8"))[0]
            raise FileNotFoundError("PKG-INFO not found in sdist")

        raise ValueError(f"Unsupported file type: {file}")

    def _is_file_exists_error(self, response: requests.Response) -> bool:
        # based on https://github.com/pypa/twine/blob/a6dd69c79f7b5abfb79022092a5d3776a499e31b/twine/commands/upload.py#L32
        status = response.status_code
        reason = response.reason.lower()
        text = response.text.lower()
        reason_and_text = reason + text

        return (
            # pypiserver (https://pypi.org/project/pypiserver)
            status == 409
            # PyPI / TestPyPI / GCP Artifact Registry
            or (status == 400 and "already exist" in reason_and_text)
            # Nexus Repository OSS (https://www.sonatype.com/nexus-repository-oss)
            or (status == 400 and "updating asset" in reason_and_text)
            or (status == 400 and "cannot be updated" in reason_and_text)

View on GitHub (pinned to 92b74dcfe3)

Solutions

  1. Rebuild the sdist: `poetry build -f sdist` (or `python -m build --sdist`).
  2. Inspect with `tar tzf file.tar.gz` to confirm a top-level <pkg>-<ver>/PKG-INFO entry.
  3. Avoid rezipping or restructuring sdist tarballs; regenerate from source.
  4. Verify the build backend produces PEP 517-compliant sdists.

Example fix

// before: sdist missing PKG-INFO
$ tar tzf dist/mypkg-1.0.0.tar.gz  # no PKG-INFO
FileNotFoundError: PKG-INFO not found in sdist

// after
$ rm dist/*.tar.gz
$ poetry build -f sdist
Defensive patterns

Strategy: validation

Validate before calling

import tarfile
from pathlib import Path

def sdist_has_pkg_info(path: Path) -> bool:
    with tarfile.open(path, "r:gz") as tar:
        return any(
            len(Path(m.name).parts) == 2 and Path(m.name).parts[1] == "PKG-INFO"
            for m in tar.getmembers()
        )

Prevention

When it happens

Trigger: A .tar.gz sdist is processed (suffixes .tar.gz pass the type check) but the archive contains no PKG-INFO at the expected two-part path — e.g. a tarball that is not a real Python sdist, or one whose top-level dir was restructured.

Common situations: A tarball repackaged with a different top-level directory layout; a sdist whose PKG-INFO was stripped; a .tar.gz that is source code but not a proper sdist; a build backend bug that omitted PKG-INFO.

Related errors


AI-assisted analysis of python-poetry/poetry@92b74dcfe3 (2026-08-04). Data as JSON: /data/errors/08b6a51e7d92a4f8.json. Report an issue: GitHub.