pypa/pip · error · IDNAError
should pass a unicode string to the function rather than a b
Error message
should pass a unicode string to the function rather than a byte string.
What it means
Raised by encode() (core.py:549) when the input is not a str and cannot be decoded as ASCII — i.e. the caller passed a bytes/bytearray containing non-ASCII bytes. idna.encode() expects a Unicode (str) domain; bytes input is only tolerated if it is pure ASCII, otherwise it raises IDNAError with this message. The original UnicodeDecodeError/TypeError is chained as the cause.
Source
Thrown at src/pip/_vendor/idna/core.py:549
:param transitional: Forwarded to :func:`uts46_remap` when ``uts46``
is ``True``. Deprecated: emits a :class:`DeprecationWarning` and
will be removed in a future version.
:returns: The encoded domain as ASCII :class:`bytes`.
:raises IDNAError: If the domain is empty, contains an invalid label,
or exceeds the maximum domain length.
"""
if transitional:
warnings.warn(
"Transitional processing has been removed from UTS #46. "
"The transitional argument will be removed in a future version.",
DeprecationWarning,
stacklevel=2,
)
if not isinstance(s, str):
try:
s = str(s, "ascii")
except (UnicodeDecodeError, TypeError) as err:
raise IDNAError("should pass a unicode string to the function rather than a byte string.") from err
if len(s) > _max_input_length:
raise IDNAError("Domain too long")
if uts46:
s = uts46_remap(s, std3_rules, transitional)
# Reject inputs that exceed the maximum DNS domain length up-front
# to avoid expensive computation on long inputs.
if not valid_string_length(s, trailing_dot=True):
raise IDNAError("Domain too long")
trailing_dot = False
result = []
labels = s.split(".") if strict else _unicode_dots_re.split(s)
if not labels or labels == [""]:
raise IDNAError("Empty domain")
if labels[-1] == "":
del labels[-1]
trailing_dot = TrueView on GitHub (pinned to d7d0d0a394)
Solutions
- Decode the bytes to str with the correct encoding (usually UTF-8) before calling encode: idna.encode(s.decode('utf-8')).
- Make the caller-side type explicit: accept only str at your function boundary and coerce/validate early.
- If the value really is an ASCII A-label in bytes, decode as ASCII first: s.decode('ascii').
Example fix
// before
idna.encode(b'münchen.com') # bytes with non-ASCII -> IDNAError
// after
idna.encode('münchen.com') # pass a str
# or, coming from bytes:
idna.encode(raw.decode('utf-8')) Defensive patterns
Strategy: type-guard
Validate before calling
def to_unicode_domain(s):
if isinstance(s, (bytes, bytearray)):
return s.decode('utf-8')
if not isinstance(s, str):
raise TypeError(f'expected str or bytes, got {type(s).__name__}')
return s
encoded = idna.encode(to_unicode_domain(raw)) Type guard
def is_unicode_str(s) -> bool:
return isinstance(s, str) Try / catch
import idna
try:
encoded = idna.encode(raw)
except idna.IDNAError as err:
if 'unicode string' in str(err):
# raw was bytes with non-ASCII; decode and retry
encoded = idna.encode(raw.decode('utf-8'))
else:
raise Prevention
- Always decode bytes to str (UTF-8) at the boundary before passing to idna.
- Type-annotate domain parameters as str and enforce with a runtime check.
- Open files holding domains in text mode ('r', encoding='utf-8'), not binary.
When it happens
Trigger: Calling idna.encode() with bytes that contain non-ASCII octets, e.g. idna.encode(b'münchen.com'), idna.encode(some_bytes_variable), or passing a UTF-8-encoded byte string read from a file/socket without decoding first.
Common situations: Reading domains from binary sources (files opened 'rb', network sockets, database BLOB columns) and forgetting to .decode('utf-8'); mixing str/bytes across an API boundary; porting code that previously handled bytes; or framework code that passes request bodies as bytes into a URL/IDNA path.
Related errors
- Invalid ASCII in A-label
- Codepoint {_unot(cp_value)} not allowed at position {pos + 1
- Codepoint {_unot(cp_value)} at position {pos + 1} of {label!
- Malformed A-label, no Punycode eligible content found
- A-label must not end with a hyphen
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/f2123c07f13067f3.json.
Report an issue: GitHub.