pypa/pip · error · DirectUrlValidationError
Unexpected type (expected )
Error message
Unexpected type {type(value).__name__} (expected {expected_type.__name__}) What it means
_get() is a helper used by DirectUrl.from_dict() and related _from_dict methods to retrieve and type-check fields from a PEP 610 direct_url.json mapping. If a value exists but its Python type does not match the expected type, DirectUrlValidationError is raised with the actual and expected type names and the field key as context. This is the primary type-validation guard for the direct URL JSON structure.
Solutions
- Ensure all fields in direct_url.json match PEP 610 types: strings for url/vcs/commit_id, bool for editable, objects for info blocks
- Validate the JSON dict types before passing to from_dict
- Use to_dict() to generate correctly-typed JSON rather than hand-crafting it
Example fix
# before
data = {"url": 12345, "dir_info": {"editable": "true"}} # wrong types
DirectUrl.from_dict(data) # raises
# after
data = {"url": "file:///path/to/project", "dir_info": {"editable": True}}
DirectUrl.from_dict(data) Defensive patterns
Strategy: validation
Validate before calling
def validate_direct_url_types(d: dict) -> None:
if "url" in d and not isinstance(d["url"], str):
raise TypeError("url must be a string")
if "subdirectory" in d and not isinstance(d["subdirectory"], str):
raise TypeError("subdirectory must be a string")
for key in ("archive_info", "vcs_info", "dir_info"):
if key in d and not isinstance(d[key], dict):
raise TypeError(f"{key} must be a dict/object")
if "dir_info" in d and "editable" in d["dir_info"]:
if not isinstance(d["dir_info"]["editable"], bool):
raise TypeError("dir_info.editable must be a bool") Type guard
def is_valid_direct_url_dict(d) -> bool:
return (
isinstance(d, dict)
and isinstance(d.get("url", ""), str)
and all(
isinstance(d.get(k), dict)
for k in ("archive_info", "vcs_info", "dir_info")
if k in d
)
) Try / catch
from packaging.direct_url import DirectUrl, DirectUrlValidationError
try:
du = DirectUrl.from_dict(data)
except DirectUrlValidationError as e:
print(f"Invalid direct_url.json: {e}") Prevention
- Never hand-edit direct_url.json; use to_dict()
- Validate JSON types before parsing
- Schema-validate PEP 610 metadata files
When it happens
Trigger: Calling DirectUrl.from_dict(d) where a field has the wrong JSON type: url is a number, subdirectory is a list, editable is a string 'true' instead of boolean true, or vcs/commit_id are non-strings.
Common situations: Hand-edited direct_url.json files. Broken pip metadata in installed packages. Programmatic generation of direct URL JSON without type checking.
Related errors
- Exactly one of vcs_info, archive_info, dir_info must be…
- File URL must be absolute when dir_info is present
- URL scheme must be file:// when dir_info is present
- Algorithm used in hash field has different value in hashes…
- Algorithm used in hash field is not present in hashes field
AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08).
Data as JSON: /api/errors/21bf6f737f102a10.
Report an issue: GitHub.
Appendix: source
Thrown at src/pip/_vendor/packaging/direct_url.py:52
class _FromMappingProtocol(Protocol): # pragma: no cover
@classmethod
def _from_dict(cls, d: Mapping[str, Any]) -> Self: ...
_FromMappingProtocolT = TypeVar("_FromMappingProtocolT", bound=_FromMappingProtocol)
def _json_dict_factory(data: list[tuple[str, Any]]) -> dict[str, Any]:
return {key: value for key, value in data if value is not None}
def _get(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T | None:
"""Get a value from the dictionary and verify it's the expected type."""
if (value := d.get(key)) is None:
return None
if not isinstance(value, expected_type):
raise DirectUrlValidationError(
f"Unexpected type {type(value).__name__} "
f"(expected {expected_type.__name__})",
context=key,
)
return value
def _get_required(d: Mapping[str, Any], expected_type: type[_T], key: str) -> _T:
"""Get a required value from the dictionary and verify it's the expected type."""
if (value := _get(d, expected_type, key)) is None:
raise _DirectUrlRequiredKeyError(key)
return value
def _get_object(
d: Mapping[str, Any], target_type: type[_FromMappingProtocolT], key: str
) -> _FromMappingProtocolT | None:
"""Get a dictionary value from the dictionary and convert it to a dataclass."""View on GitHub (pinned to f399c37189)