psf/requests · error · InvalidHeader

Header part ( ) from must be of type str or bytes, not

Error message

Header part ({header_part!r}) from {header} must be of type str or bytes, not {type(header_part)}

What it means

Raised by _validate_header_part() when a header name or value is neither str nor bytes (e.g., int, float, None, list). This is a runtime type guard — the type system expects str | bytes, but untyped callers can pass other types. It fires during request preparation before the request is sent.

Solutions

  1. Convert all header values to strings: headers={k: str(v) for k, v in headers.items()}.
  2. Filter out None values before setting headers: {k: v for k, v in headers.items() if v is not None}.
  3. Use a helper to normalize header dicts before passing to requests.

Example fix

# before
requests.get(url, headers={'Content-Length': 123, 'X-Token': None})

# after
headers = {k: str(v) for k, v in {'Content-Length': 123, 'X-Token': None}.items() if v is not None}
requests.get(url, headers=headers)
Defensive patterns

Strategy: type-guard

Validate before calling

clean_headers = {
    str(k): str(v)
    for k, v in headers.items()
    if v is not None
}
resp = requests.get(url, headers=clean_headers)

Type guard

def has_valid_header_types(headers: dict) -> bool:
    return all(
        isinstance(k, (str, bytes)) and isinstance(v, (str, bytes))
        for k, v in headers.items()
    )

Prevention

When it happens

Trigger: Passing headers={'Content-Length': 123} (int value); headers={None: 'value'} (None key); headers={'X-Count': 3.14} (float); any header dict where name or value is not str or bytes.

Common situations: Computed numeric values passed directly as header values without str() conversion; JSON-parsed data used as headers without type coercion; None values from optional config passed through without filtering.

Related errors


AI-assisted analysis of psf/requests@8068356288 (2026-08-11). Data as JSON: /api/errors/3463071ff348f5e6. Report an issue: GitHub.

Appendix: source

Thrown at src/requests/utils.py:1109

    :param header: tuple, in the format (name, value).
    """
    name, value = header
    _validate_header_part(header, name, 0)
    _validate_header_part(header, value, 1)


def _validate_header_part(
    header: tuple[str | bytes, str | bytes],
    header_part: str | bytes,
    header_validator_index: int,
) -> None:
    if isinstance(header_part, str):
        validator = _HEADER_VALIDATORS_STR[header_validator_index]
    elif isinstance(header_part, bytes):
        # runtime guard for non-str/bytes input
        validator = _HEADER_VALIDATORS_BYTE[header_validator_index]
    else:
        raise InvalidHeader(
            f"Header part ({header_part!r}) from {header} "
            f"must be of type str or bytes, not {type(header_part)}"
        )

    if not validator.match(header_part):  # type: ignore[arg-type]
        header_kind = "name" if header_validator_index == 0 else "value"
        raise InvalidHeader(
            f"Invalid leading whitespace, reserved character(s), or return "
            f"character(s) in header {header_kind}: {header_part!r}"
        )


def urldefragauth(url: str) -> str:
    """
    Given a url remove the fragment and the authentication part.

    :rtype: str
    """

View on GitHub (pinned to 8068356288)