pypa/pip · error · DirectUrlValidationError

Unexpected type {type(value).__name__} (expected {expected_t

Error message

Unexpected type {type(value).__name__} (expected {expected_type.__name__})

What it means

Raised by `packaging.direct_url._get` when a key is present in the JSON dict but its value is not of the expected type (e.g. `url` is a list instead of str, `editable` is a string instead of bool). This is the schema-validation guard for PEP 610 direct URL JSON, normally wrapped into a `DirectUrlValidationError` with the offending key as `context`.

Source

Thrown at src/pip/_vendor/packaging/direct_url.py:51

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 d7d0d0a394)

Solutions

  1. Reinstall the package so pip regenerates a spec-compliant `direct_url.json`
  2. Inspect `<env>/site-packages/<dist>-*.dist-info/direct_url.json` and fix the offending key's type
  3. Validate the dict against PEP 610 schema before calling `from_dict`

Example fix

// before
DirectUrl.from_dict({'url': 123, 'archive_info': {}})
// after
DirectUrl.from_dict({'url': 'https://example.com/pkg.tar.gz', 'archive_info': {}})
Defensive patterns

Strategy: validation

Validate before calling

def validate_direct_url_dict(d: dict) -> None:
    if 'url' in d and not isinstance(d['url'], str):
        raise ValueError('url must be str')
    if 'subdirectory' in d and not isinstance(d['subdirectory'], str):
        raise ValueError('subdirectory must be str')

Type guard

def is_direct_url_compatible(d: object) -> bool:
    return isinstance(d, dict) and all(
        (k not in d) or isinstance(d[k], t)
        for k, t in (('url', str), ('subdirectory', str))
    )

Try / catch

from packaging.direct_url import DirectUrl, DirectUrlValidationError
try:
    du = DirectUrl.from_dict(d)
except DirectUrlValidationError as e:
    raise ValueError(f'bad direct_url.json: {e}') from e

Prevention

When it happens

Trigger: Calling `DirectUrl.from_dict(d)` (or pip loading a `direct_url.json` from an installed dist) where `d['url']` is not a str, `d['subdirectory']` is not a str, `d['vcs_info']['commit_id']` is not a str, or `d['dir_info']['editable']` is not a bool.

Common situations: Hand-edited `direct_url.json` in site-packages; a tool that emits JSON with coerced types (e.g. quoting booleans); a corrupted install record after an interrupted pip install; a custom installer that writes non-spec records.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/21bf6f737f102a10.json. Report an issue: GitHub.