{"record":{"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/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_internal/operations/prepare.py#L458-L494","documentation":"Raised by _get_linked_req_hashes (prepare.py:475) as DirectoryUrlHashUnsupported when --require-hashes mode encounters a file:// URL pointing at an existing directory. A directory has no single byte stream to hash, so pip cannot satisfy hash verification for directory-based file:// requirements.","triggerScenarios":"Installing from a local directory via 'file:///path/to/project' (or a bare local path resolved to file:) while --require-hashes is active. Detected via req.link.is_existing_dir().","commonSituations":"A hashed requirements file referencing a local editable/directory install; CI that pins hashes but also installs an in-repo package by path.","solutions":["Build the local project into a wheel/sdist first, then reference the artifact with its hash.","Split the install so the local directory is installed without --require-hashes.","Use an sdist (.tar.gz) of the project and pin its hash instead of the directory."],"exampleFix":"# before\npip install --require-hashes file:///path/to/myproject\n# after\npython -m build /path/to/myproject\npip install --require-hashes /path/to/myproject/dist/myproject-1.0.tar.gz --hash sha256:...","handlingStrategy":"validation","validationCode":"import os\n\ndef is_dir_file_url(line: str) -> bool:\n    s = line.split('#')[0].strip()\n    if s.lower().startswith('file:'):\n        path = s[5:].lstrip('/')\n        return os.path.isdir('/' + path if not os.path.isabs(path) else path)\n    return os.path.isdir(s) and not s.endswith(('.whl', '.tar.gz', '.zip'))\n\ndef no_dir_file_urls_in_hashed_file(path: str) -> bool:\n    with open(path) as f:\n        return not any(is_dir_file_url(l) for l in f if l.strip())","typeGuard":"import os\n\ndef is_installable_artifact_path(path: str) -> bool:\n    return os.path.isfile(path) and path.endswith(('.whl', '.tar.gz', '.zip'))","tryCatchPattern":"from subprocess import run, CalledProcessError\ntry:\n    run([\"pip\", \"install\", \"--require-hashes\", \"-r\", req_file], check=True)\nexcept CalledProcessError:\n    # Fallback: build the directory into a wheel and hash it.\n    run([\"python\", \"-m\", \"build\", \"--wheel\", project_dir], check=True)","preventionTips":["Build local projects to wheels/sdists and reference artifacts by hash instead of directories.","Audit hashed requirement files for file:// directory references.","Split directory installs out of --require-hashes runs."],"tags":["require-hashes","file-url","directory","hashing","security","pip"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}