encode/httpx · error · TypeError
Header value must be str or bytes, not {type(value)}
Error message
Header value must be str or bytes, not {type(value)} What it means
TypeError raised by _normalize_header_value when a header value is neither str nor bytes. httpx headers are normalized to bytes, so any other type (int, None, dict, list, custom object) is rejected. This fires during Headers construction or any header mutation that funnels through _normalize_header_value (e.g. client.headers['X-Count'] = 5).
Source
Thrown at httpx/_models.py:81
return False
return True
def _normalize_header_key(key: str | bytes, encoding: str | None = None) -> bytes:
"""
Coerce str/bytes into a strictly byte-wise HTTP header key.
"""
return key if isinstance(key, bytes) else key.encode(encoding or "ascii")
def _normalize_header_value(value: str | bytes, encoding: str | None = None) -> bytes:
"""
Coerce str/bytes into a strictly byte-wise HTTP header value.
"""
if isinstance(value, bytes):
return value
if not isinstance(value, str):
raise TypeError(f"Header value must be str or bytes, not {type(value)}")
return value.encode(encoding or "ascii")
def _parse_content_type_charset(content_type: str) -> str | None:
# We used to use `cgi.parse_header()` here, but `cgi` became a dead battery.
# See: https://peps.python.org/pep-0594/#cgi
msg = email.message.Message()
msg["content-type"] = content_type
return msg.get_content_charset(failobj=None)
def _parse_header_links(value: str) -> list[dict[str, str]]:
"""
Returns a list of parsed link headers, for more info see:
https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Link
The generic syntax of those is:
Link: < uri-reference >; param1=value1; param2="value2"
So for instance:View on GitHub (pinned to b5addb64f0)
Solutions
- Coerce to str: client.headers['X-Count'] = str(count).
- For None, use a guard: headers['X-Opt'] = value if value is not None else ''.
- For lists, join: ', '.join(values).
- For datetimes, .isoformat() or format explicitly.
Example fix
// before client.headers['X-Retry-Count'] = retries # int -> TypeError // after client.headers['X-Retry-Count'] = str(retries)
Defensive patterns
Strategy: type-guard
Validate before calling
def coerce_header_value(value):
if isinstance(value, (str, bytes)):
return value
if value is None:
return ''
return str(value)
headers = {k: coerce_header_value(v) for k, v in raw_headers.items()} Type guard
def is_valid_header_value(value) -> bool:
return isinstance(value, (str, bytes)) Try / catch
try:
client.headers['X-Count'] = count
except TypeError:
client.headers['X-Count'] = str(count) Prevention
- Always coerce header values to str/bytes at the boundary.
- Handle None explicitly (empty string or skip).
- Join lists with ', ' rather than passing them raw.
- Format datetimes/objects before assigning to headers.
When it happens
Trigger: Setting a header to an int (X-Total: 5), float, None, bool, or list; passing a Headers object/dict whose values are non-string; client(headers={'X-Retry': 3}); response.headers manipulation with numeric counters.
Common situations: Dynamic header values computed from numeric data without str(); None passed where a string was expected (e.g. missing config); booleans from toggles; CSV lists meant to be joined; datetime objects not isoformatted.
Related errors
- Unexpected type for 'content', {type(content)!r}
- {key}
- Attempted to read or stream some content, but the content ha
- Invalid type for name. Expected str, got {type(name)}: {name
- Invalid type for value. Expected primitive type, got {type(v
AI-assisted analysis of encode/httpx@b5addb64f0 (2026-08-04).
Data as JSON: /data/errors/c6a02126cc4ae39c.json.
Report an issue: GitHub.