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
- Rebuild the sdist: `poetry build -f sdist` (or `python -m build --sdist`).
- Inspect with `tar tzf file.tar.gz` to confirm a top-level <pkg>-<ver>/PKG-INFO entry.
- Avoid rezipping or restructuring sdist tarballs; regenerate from source.
- 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
- Build sdists with a PEP 517 backend that emits PKG-INFO.
- Do not restructure sdist tarballs after building.
- Inspect sdist contents (tar tzf) in CI before publishing.
- Rebuild suspect sdists from source.
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
- METADATA not found in wheel
- Archive ({file}) does not exist
- Unknown distribution format {exts}
- Invalid build config setting '{value}'. It must be a valid J
- Invalid author string. Must be in the format: John Smith <jo
AI-assisted analysis of python-poetry/poetry@92b74dcfe3 (2026-08-04).
Data as JSON: /data/errors/08b6a51e7d92a4f8.json.
Report an issue: GitHub.