{"record":{"id":"1c2c0784d5adde78","repo":"pypa/pip","slug":"url-scheme-must-be-file-when-dir-info-is-presen","errorCode":null,"errorMessage":"URL scheme must be file:// when dir_info is present","messagePattern":"URL scheme must be file:// when dir_info is present","errorType":"validation","errorClass":"DirectUrlValidationError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/direct_url.py","lineNumber":300,"sourceCode":"        direct_url = cls(\n            url=_get_required(d, str, \"url\"),\n            archive_info=_get_object(d, ArchiveInfo, \"archive_info\"),\n            vcs_info=_get_object(d, VcsInfo, \"vcs_info\"),\n            dir_info=_get_object(d, DirInfo, \"dir_info\"),\n            subdirectory=_get(d, str, \"subdirectory\"),\n        )\n        if (\n            bool(direct_url.vcs_info)\n            + bool(direct_url.archive_info)\n            + bool(direct_url.dir_info)\n        ) != 1:\n            raise DirectUrlValidationError(\n                \"Exactly one of vcs_info, archive_info, dir_info must be present\"\n            )\n        if direct_url.dir_info is not None:\n            parsed_url = urllib.parse.urlsplit(direct_url.url)\n            if parsed_url.scheme != \"file\":\n                raise DirectUrlValidationError(\n                    \"URL scheme must be file:// when dir_info is present\",\n                    context=\"url\",\n                )\n            if not _file_url_has_absolute_path(parsed_url):\n                raise DirectUrlValidationError(\n                    \"File URL must be absolute when dir_info is present\",\n                    context=\"url\",\n                )\n        # XXX subdirectory must be relative, can we, should we validate that here?\n        return direct_url\n\n    @classmethod\n    def from_dict(cls, d: Mapping[str, Any], /) -> Self:\n        \"\"\"Create and validate a DirectUrl instance from a JSON dictionary.\"\"\"\n        return cls._from_dict(d)\n\n    def to_dict(\n        self,","sourceCodeStart":282,"sourceCodeEnd":318,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/direct_url.py#L282-L318","documentation":"When dir_info is present in a direct URL, the source is a local directory. PEP 610 requires the URL scheme to be file://. If the URL has a different scheme (e.g. https://, git+https://), DirectUrlValidationError is raised because a directory source cannot have a remote URL scheme.","triggerScenarios":"Calling DirectUrl.from_dict(data) with dir_info present but the url field using a non-file scheme: e.g. {'url': 'https://example.com/path', 'dir_info': {'editable': true}}.","commonSituations":"Using dir_info when the source is actually a remote archive or VCS repo (wrong info block type). Hand-crafted metadata with a mismatched URL scheme and info block.","solutions":["If the source is truly a local directory, use a file:// URL (e.g. via pathlib.Path.as_uri())","If the source is remote, use archive_info or vcs_info instead of dir_info","Fix the URL scheme to match the info block type"],"exampleFix":"# before\ndata = {\n    \"url\": \"https://example.com/local/project\",\n    \"dir_info\": {\"editable\": True}\n}\n\n# after (if truly local)\nfrom pathlib import Path\ndata = {\n    \"url\": Path(\"/local/project\").as_uri(),\n    \"dir_info\": {\"editable\": True}\n}\n# or if remote, use archive_info:\ndata = {\n    \"url\": \"https://example.com/project.tar.gz\",\n    \"archive_info\": {\"hashes\": {\"sha256\": \"...\"}}\n}","handlingStrategy":"validation","validationCode":"import urllib.parse\n\ndef validate_dir_info_url(data: dict) -> None:\n    if data.get(\"dir_info\") is not None:\n        scheme = urllib.parse.urlsplit(data[\"url\"]).scheme\n        if scheme != \"file\":\n            raise ValueError(f\"dir_info requires file:// URL, got scheme: {scheme!r}\")","typeGuard":"import urllib.parse\n\ndef is_valid_dir_url(data: dict) -> bool:\n    if data.get(\"dir_info\") is None:\n        return True\n    return urllib.parse.urlsplit(data[\"url\"]).scheme == \"file\"","tryCatchPattern":"from packaging.direct_url import DirectUrl, DirectUrlValidationError\n\ntry:\n    du = DirectUrl.from_dict(data)\nexcept DirectUrlValidationError as e:\n    if \"scheme must be file\" in str(e):\n        # convert to file URL or switch info block type\n        pass","preventionTips":["Use pathlib.Path.as_uri() to generate correct file:// URLs","Match info block type to the actual source type","Validate URL scheme consistency with info blocks"],"tags":["packaging","pep610","json-validation","direct-url","url","vendored"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}