{"id":"1e08e4a815464be4","repo":"python-poetry/poetry","slug":"metadata-not-found-in-wheel","errorCode":null,"errorMessage":"METADATA not found in wheel","messagePattern":"METADATA not found in wheel","errorType":"exception","errorClass":"FileNotFoundError","httpStatus":null,"severity":"error","filePath":"src/poetry/publishing/uploader.py","lineNumber":352,"sourceCode":"        elif len(exts) >= 2 and \"\".join(exts[-2:]) == \".tar.gz\":\n            return \"sdist\"\n\n        raise ValueError(\"Unknown distribution format \" + \"\".join(exts))\n\n    @staticmethod\n    def _get_metadata(file: Path) -> RawMetadata:\n        if file.suffix == \".whl\":\n            with zipfile.ZipFile(file) as z:\n                for name in z.namelist():\n                    parts = Path(name).parts\n                    if (\n                        len(parts) == 2\n                        and parts[1] == \"METADATA\"\n                        and parts[0].endswith(\".dist-info\")\n                    ):\n                        with z.open(name) as mf:\n                            return parse_email(mf.read().decode(\"utf-8\"))[0]\n            raise FileNotFoundError(\"METADATA not found in wheel\")\n\n        elif file.suffixes[-2:] == [\".tar\", \".gz\"]:\n            with tarfile.open(file, \"r:gz\") as tar:\n                for member in tar.getmembers():\n                    parts = Path(member.name).parts\n                    if (\n                        len(parts) == 2\n                        and parts[1] == \"PKG-INFO\"\n                        and (pf := tar.extractfile(member))\n                    ):\n                        return parse_email(pf.read().decode(\"utf-8\"))[0]\n            raise FileNotFoundError(\"PKG-INFO not found in sdist\")\n\n        raise ValueError(f\"Unsupported file type: {file}\")\n\n    def _is_file_exists_error(self, response: requests.Response) -> bool:\n        # based on https://github.com/pypa/twine/blob/a6dd69c79f7b5abfb79022092a5d3776a499e31b/twine/commands/upload.py#L32\n        status = response.status_code","sourceCodeStart":334,"sourceCodeEnd":370,"githubUrl":"https://github.com/python-poetry/poetry/blob/92b74dcfe348d0e01e14d40d6c1fa47a4ee04a54/src/poetry/publishing/uploader.py#L334-L370","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Rebuild the wheel cleanly: `poetry build -f wheel` (or `python -m build --wheel`).","Inspect the wheel contents with `unzip -l file.whl` to confirm a *.dist-info/METADATA exists.","Do not hand-edit or rezip wheels; regenerate them from source.","If the wheel came from elsewhere, discard it and rebuild from your project."],"exampleFix":"// before: malformed wheel\n$ unzip -l dist/mypkg-1.0.0-py3-none-any.whl  # no dist-info/METADATA\nFileNotFoundError: METADATA not found in wheel\n\n// after\n$ rm dist/*.whl\n$ poetry build -f wheel","handlingStrategy":"validation","validationCode":"import zipfile\nfrom pathlib import Path\n\ndef wheel_has_metadata(path: Path) -> bool:\n    with zipfile.ZipFile(path) as z:\n        return any(\n            len(Path(n).parts) == 2\n            and Path(n).parts[1] == \"METADATA\"\n            and Path(n).parts[0].endswith(\".dist-info\")\n            for n in z.namelist()\n        )","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Build wheels with a standard backend (poetry/hatchling/setuptools).","Do not rezip or hand-edit wheels.","Validate wheel contents (unzip -l) in CI before publishing.","Discard suspect wheels and rebuild from source."],"tags":["publishing","wheel","metadata","build"],"analyzedSha":"92b74dcfe348d0e01e14d40d6c1fa47a4ee04a54","analyzedAt":"2026-08-04T20:33:34.072Z","schemaVersion":2}