{"record":{"id":"8948fa5784afc85b","repo":"pypa/pip","slug":"file-url-must-be-absolute-when-dir-info-is-present","errorCode":null,"errorMessage":"File URL must be absolute when dir_info is present","messagePattern":"File URL must be absolute when dir_info is present","errorType":"validation","errorClass":"DirectUrlValidationError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/direct_url.py","lineNumber":305,"sourceCode":"            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,\n        *,\n        generate_legacy_hash: bool = False,\n        strip_user_password: bool = True,\n        safe_user_passwords: Collection[str] = (\"git\",),\n    ) -> Mapping[str, Any]:","sourceCodeStart":287,"sourceCodeEnd":323,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/direct_url.py#L287-L323","documentation":"When dir_info is present and the URL scheme is file, the path component must be absolute (start with /). A relative file URL like file:relative/path is invalid because directory sources must point to an unambiguous filesystem location. If the path does not start with /, DirectUrlValidationError is raised.","triggerScenarios":"Calling DirectUrl.from_dict(data) with dir_info and a file URL that has a relative path: e.g. {'url': 'file:relative/path', 'dir_info': {}}.","commonSituations":"Constructing file URLs manually without leading slashes on Unix or without proper drive/UNC format on Windows. Using string concatenation (file: + relative_path) instead of Path.as_uri().","solutions":["Use pathlib.Path(path).resolve().as_uri() to generate correct absolute file URLs","Ensure the path starts with / on Unix or uses proper Windows drive format","Fix the URL to be absolute before parsing"],"exampleFix":"# before\ndata = {\n    \"url\": \"file:relative/path/to/project\",\n    \"dir_info\": {\"editable\": True}\n}\n\n# after\nfrom pathlib import Path\ndata = {\n    \"url\": Path(\"relative/path/to/project\").resolve().as_uri(),\n    \"dir_info\": {\"editable\": True}\n}","handlingStrategy":"validation","validationCode":"import urllib.parse\nfrom pathlib import Path\n\ndef validate_file_url_absolute(data: dict) -> None:\n    if data.get(\"dir_info\") is not None:\n        parsed = urllib.parse.urlsplit(data[\"url\"])\n        if parsed.scheme == \"file\" and not parsed.path.startswith(\"/\"):\n            raise ValueError(f\"File URL must be absolute, got: {data['url']!r}\")\n\ndef make_file_url(path: str) -> str:\n    return Path(path).resolve().as_uri()","typeGuard":"import urllib.parse\n\ndef is_absolute_file_url(url: str) -> bool:\n    parsed = urllib.parse.urlsplit(url)\n    return parsed.scheme != \"file\" or parsed.path.startswith(\"/\")","tryCatchPattern":"from packaging.direct_url import DirectUrl, DirectUrlValidationError\nfrom pathlib import Path\n\ntry:\n    du = DirectUrl.from_dict(data)\nexcept DirectUrlValidationError as e:\n    if \"must be absolute\" in str(e):\n        raw_path = data[\"url\"].replace(\"file:\", \"\")\n        data[\"url\"] = Path(raw_path).resolve().as_uri()\n        du = DirectUrl.from_dict(data)","preventionTips":["Always use Path.resolve().as_uri() for file URLs","Never construct file:// URLs by string concatenation","Test URL generation on Windows where path formats differ"],"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-14T05:17:10.506Z"}