encode/httpx · error · TypeError

Invalid type for name. Expected str, got {type(name)}: {name

Error message

Invalid type for name. Expected str, got {type(name)}: {name!r}

What it means

Raised as `TypeError` by `DataField.__init__` when the multipart field `name` is not a `str`. Multipart form field names must be text (they become the `name=` parameter in the `Content-Disposition` header), so passing bytes, an int, or None for the field name is rejected up front.

Source

Thrown at httpx/_multipart.py:77

    if not content_type or not content_type.startswith(b"multipart/form-data"):
        return None
    # parse boundary according to
    # https://www.rfc-editor.org/rfc/rfc2046#section-5.1.1
    if b";" in content_type:
        for section in content_type.split(b";"):
            if section.strip().lower().startswith(b"boundary="):
                return section.strip()[len(b"boundary=") :].strip(b'"')
    return None


class DataField:
    """
    A single form field item, within a multipart form field.
    """

    def __init__(self, name: str, value: str | bytes | int | float | None) -> None:
        if not isinstance(name, str):
            raise TypeError(
                f"Invalid type for name. Expected str, got {type(name)}: {name!r}"
            )
        if value is not None and not isinstance(value, (str, bytes, int, float)):
            raise TypeError(
                "Invalid type for value. Expected primitive type,"
                f" got {type(value)}: {value!r}"
            )
        self.name = name
        self.value: str | bytes = (
            value if isinstance(value, bytes) else primitive_value_to_str(value)
        )

    def render_headers(self) -> bytes:
        if not hasattr(self, "_headers"):
            name = _format_form_param("name", self.name)
            self._headers = b"".join(
                [b"Content-Disposition: form-data; ", name, b"\r\n\r\n"]
            )

View on GitHub (pinned to b5addb64f0)

Solutions

  1. Coerce keys to str before passing: `{str(k): v for k, v in data.items()}`.
  2. Validate the data dict shape before the request: `assert all(isinstance(k, str) for k in data)`.
  3. Convert enums: `data = {k.value: v for k, v in data.items()}` if keys are enums.
  4. Avoid mixing the same dict for JSON (which may allow non-string keys after encoding) and multipart.

Example fix

// before
client.post(url, data={user_id: 'on'})  # user_id is int -> TypeError

// after
client.post(url, data={str(user_id): 'on'})
Defensive patterns

Strategy: validation

Validate before calling

def normalize_form_keys(data: dict) -> dict:
    bad = [k for k in data if not isinstance(k, str)]
    if bad:
        raise TypeError(f'non-str form field keys: {bad!r}')
    return {str(k): v for k, v in data.items()}

Type guard

def form_keys_are_str(data: dict) -> bool:
    return all(isinstance(k, str) for k in data)

Try / catch

try:
    client.post(url, data=payload)
except TypeError:
    payload = {str(k): v for k, v in payload.items()}
    client.post(url, data=payload)

Prevention

When it happens

Trigger: Building `data={123: 'value'}` (int key) or `data={b'key': 'value'}` (bytes key) and passing it to `client.post(url, data=..., files=...)`; or constructing `httpx._multipart.DataField(123, 'v')` directly.

Common situations: Keys coming from JSON/dict sources that use non-string keys; dataclasses/enums used as dict keys without conversion; porting code that accidentally iterates `items()` of a non-string-keyed mapping.

Related errors


AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04). Data as JSON: /data/errors/3e10be3f07ab44f8.json. Report an issue: GitHub.