{"id":"d3ec188483da6b1b","repo":"pypa/pip","slug":"unexpected-file-in-wheel-path-record-path-r","errorCode":null,"errorMessage":"Unexpected file in {wheel_path}: {record_path!r}. .data directory contents should be named like: '<scheme key>/<path>'.","messagePattern":"Unexpected file in (.+?): (.+?)\\. \\.data directory contents should be named like: '<scheme key>/<path>'\\.","errorType":"exception","errorClass":"InstallationError","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/operations/install/wheel.py","lineNumber":524,"sourceCode":"            return ZipBackedFile(record_path, dest_path, zip_file)\n\n        return make_root_scheme_file\n\n    def data_scheme_file_maker(\n        zip_file: ZipFile, scheme: Scheme\n    ) -> Callable[[RecordPath], File]:\n        scheme_paths = {key: getattr(scheme, key) for key in SCHEME_KEYS}\n\n        def make_data_scheme_file(record_path: RecordPath) -> File:\n            normed_path = os.path.normpath(record_path)\n            try:\n                _, scheme_key, dest_subpath = normed_path.split(os.path.sep, 2)\n            except ValueError:\n                message = (\n                    f\"Unexpected file in {wheel_path}: {record_path!r}. .data directory\"\n                    \" contents should be named like: '<scheme key>/<path>'.\"\n                )\n                raise InstallationError(message)\n\n            try:\n                scheme_path = scheme_paths[scheme_key]\n            except KeyError:\n                valid_scheme_keys = \", \".join(sorted(scheme_paths))\n                message = (\n                    f\"Unknown scheme key used in {wheel_path}: {scheme_key} \"\n                    f\"(for file {record_path!r}). .data directory contents \"\n                    f\"should be in subdirectories named with a valid scheme \"\n                    f\"key ({valid_scheme_keys})\"\n                )\n                raise InstallationError(message)\n\n            dest_path = os.path.join(scheme_path, dest_subpath)\n            assert_no_path_traversal(scheme_path, dest_path)\n            return ZipBackedFile(record_path, dest_path, zip_file)\n\n        return make_data_scheme_file","sourceCodeStart":506,"sourceCodeEnd":542,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_internal/operations/install/wheel.py#L506-L542","documentation":"Raised as InstallationError at wheel.py:518-524 when a file path under the wheel's *.data directory cannot be split into the expected '<scheme key>/<subpath>' form (the normed path split on os.path.sep yields fewer than 3 parts, raising ValueError). This means a .data entry is missing its required scheme-key subdirectory.","triggerScenarios":"A wheel places a file directly under '<name>-<ver>.data/' with no scheme-key subdir, e.g. 'mypkg-1.0.data/README' instead of 'mypkg-1.0.data/scripts/README'. The split into ('.data', scheme_key, subpath) fails.","commonSituations":"A custom or hand-rolled wheel that doesn't follow the .data directory layout. A buggy build backend that emits stray files at the .data root. Older/non-standard packaging tools.","solutions":["Open the wheel (it's a zip) and inspect entries under the *.data/ directory.","Ensure every file under *.data/ is nested in a valid scheme-key subdirectory (purelib, platlib, headers, scripts, data).","Rebuild the wheel with a standards-compliant backend (setuptools, hatchling, flit) and reinstall.","If third-party, pin to a version whose wheel is correctly structured."],"exampleFix":"# before (wheel layout)\nmypkg-1.0.data/cli_helper\n\n# after\nmypkg-1.0.data/scripts/cli_helper","handlingStrategy":"validation","validationCode":"import zipfile, os\nVALID_PREFIX = \".data\" + os.sep\n\ndef validate_wheel_data_layout(whl):\n    with zipfile.ZipFile(whl) as z:\n        for n in z.namelist():\n            head = n.split(os.sep, 1)[0]\n            if head.endswith(\".data\"):\n                rest = n.split(os.sep, 2)\n                if len(rest) < 3:\n                    raise ValueError(f\".data file missing scheme key/path: {n}\")","typeGuard":"def is_valid_data_path(record_path: str) -> bool:\n    parts = record_path.split(\"/\", 2)\n    return len(parts) >= 3 and parts[0].endswith(\".data\")","tryCatchPattern":null,"preventionTips":["Use a PEP 517 build backend to produce well-formed wheels.","Validate wheel layout in CI before publishing.","Never hand-edit wheels; rebuild from source."],"tags":["wheel","packaging","scheme","data-files","install"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}