{"id":"4122f4fdeec96d27","repo":"pypa/pip","slug":"can-t-verify-hashes-for-these-file-requirements","errorCode":null,"errorMessage":"Can't verify hashes for these file:// requirements because they point to directories:","messagePattern":"Can't verify hashes for these file:// requirements because they point to directories:","errorType":"exception","errorClass":"DirectoryUrlHashUnsupported","httpStatus":null,"severity":"error","filePath":"src/pip/_internal/operations/prepare.py","lineNumber":476,"sourceCode":"        req.ensure_pristine_source_checkout()\n\n    def _get_linked_req_hashes(self, req: InstallRequirement) -> Hashes:\n        # By the time this is called, the requirement's link should have\n        # been checked so we can tell what kind of requirements req is\n        # and raise some more informative errors than otherwise.\n        # (For example, we can raise VcsHashUnsupported for a VCS URL\n        # rather than HashMissing.)\n        if not self.require_hashes:\n            return req.hashes(trust_internet=True)\n\n        # We could check these first 2 conditions inside unpack_url\n        # and save repetition of conditions, but then we would\n        # report less-useful error messages for unhashable\n        # requirements, complaining that there's no hash provided.\n        if req.link.is_vcs:\n            raise VcsHashUnsupported()\n        if req.link.is_existing_dir():\n            raise DirectoryUrlHashUnsupported()\n\n        # Unpinned packages are asking for trouble when a new version\n        # is uploaded.  This isn't a security check, but it saves users\n        # a surprising hash mismatch in the future.\n        # file:/// URLs aren't pinnable, so don't complain about them\n        # not being pinned.\n        if not req.is_direct and not req.is_pinned:\n            raise HashUnpinned()\n\n        # If known-good hashes are missing for this requirement,\n        # shim it with a facade object that will provoke hash\n        # computation and then raise a HashMissing exception\n        # showing the user what the hash should be.\n        return req.hashes(trust_internet=False) or MissingHashes()\n\n    def _fetch_metadata_only(\n        self,\n        req: InstallRequirement,","sourceCodeStart":458,"sourceCodeEnd":494,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_internal/operations/prepare.py#L458-L494","documentation":"Raised as DirectoryUrlHashUnsupported when --require-hashes is active and a requirement points to a local directory via a file:// URL or an existing-directory link. At prepare.py:475-476, _get_linked_req_hashes checks req.link.is_existing_dir() and aborts because a directory has no single file to hash.","triggerScenarios":"pip install --require-hashes with a requirement like 'file:///path/to/pkgdir' or a local editable directory install while hash checking is mandatory. The directory as a whole cannot be checksummed.","commonSituations":"Hashed lockfiles that include a local source directory. CI that enforces --require-hashes but installs a vendored/monorepo package from a path.","solutions":["Build an sdist or wheel from the directory and reference it by URL with a sha256 hash.","Run pip wheel <dir> --no-deps -w ./wheels, then add the resulting wheel with its hash to the requirements file.","Disable --require-hashes for the local-directory install if hashing is not required there.","Package the local code into a proper distribution before hashing."],"exampleFix":"# before (requirements.txt with --require-hashes)\nfile:///opt/src/mylocalpkg\n\n# after: build a wheel and pin it\n./wheels/mylocalpkg-1.0-py3-none-any.whl \\\n  --hash=sha256:abcdef0123456789abcdef0123456789abcdef0123456789abcdef0123456789","handlingStrategy":"validation","validationCode":"import os, urllib.parse\ndef is_directory_url(url: str) -> bool:\n    if not url.startswith(\"file://\"):\n        return False\n    path = urllib.parse.urlparse(url).path\n    return os.path.isdir(path)\n\nfor req in requirements:\n    if require_hashes and is_directory_url(req.url):\n        raise SystemExit(f\"directory URL cannot be hashed: {req.url}\")","typeGuard":"import os, urllib.parse\ndef is_directory_url(url: str) -> bool:\n    if not url.startswith(\"file://\"):\n        return False\n    return os.path.isdir(urllib.parse.urlparse(url).path)","tryCatchPattern":null,"preventionTips":["Build a wheel from local directories before pinning by hash.","Do not combine file:// directory requirements with --require-hashes.","Package local code into a distribution for reproducible hashing."],"tags":["hashes","require-hashes","file-url","directory","security"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}