python-poetry/poetry · error · FileNotFoundError

METADATA not found in wheel

Error message

METADATA not found in wheel

What it means

Raised by Uploader._get_metadata() when a .whl zip is opened but no member matching <name>.dist-info/METADATA is found at the archive root. The METADATA file is the wheel's packaging metadata; its absence means the wheel is malformed and cannot be uploaded.

Source

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

        elif len(exts) >= 2 and "".join(exts[-2:]) == ".tar.gz":
            return "sdist"

        raise ValueError("Unknown distribution format " + "".join(exts))

    @staticmethod
    def _get_metadata(file: Path) -> RawMetadata:
        if file.suffix == ".whl":
            with zipfile.ZipFile(file) as z:
                for name in z.namelist():
                    parts = Path(name).parts
                    if (
                        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

View on GitHub (pinned to 92b74dcfe3)

Solutions

  1. Rebuild the wheel cleanly: `poetry build -f wheel` (or `python -m build --wheel`).
  2. Inspect the wheel contents with `unzip -l file.whl` to confirm a *.dist-info/METADATA exists.
  3. Do not hand-edit or rezip wheels; regenerate them from source.
  4. If the wheel came from elsewhere, discard it and rebuild from your project.

Example fix

// before: malformed wheel
$ unzip -l dist/mypkg-1.0.0-py3-none-any.whl  # no dist-info/METADATA
FileNotFoundError: METADATA not found in wheel

// after
$ rm dist/*.whl
$ poetry build -f wheel
Defensive patterns

Strategy: validation

Validate before calling

import zipfile
from pathlib import Path

def wheel_has_metadata(path: Path) -> bool:
    with zipfile.ZipFile(path) as z:
        return any(
            len(Path(n).parts) == 2
            and Path(n).parts[1] == "METADATA"
            and Path(n).parts[0].endswith(".dist-info")
            for n in z.namelist()
        )

Prevention

When it happens

Trigger: A .whl file is passed to _get_metadata (suffix .whl passes the type check) but the zip contents have no top-level *.dist-info/METADATA entry — a corrupt or hand-assembled wheel.

Common situations: A wheel built by a broken/custom build backend; a wheel repackaged incorrectly after editing; a truncated download; a wheel renamed to .whl but not actually a wheel; a wheel whose dist-info dir was stripped.

Related errors


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