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

  1. Ensure all fields in direct_url.json match PEP 610 types: strings for url/vcs/commit_id, bool for editable, objects for info blocks
  2. Validate the JSON dict types before passing to from_dict
  3. 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

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


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)