pypa/pip · error · IDNAError
Empty Label
Error message
Empty Label
What it means
IDNAError raised by check_label when, after bytes-to-str decoding, the label has length 0. An empty label is structurally invalid: DNS does not permit zero-length labels inside a name (only the trailing root label, represented separately).
Source
Thrown at src/pip/_vendor/idna/core.py:345
(:func:`check_initial_combiner`), per-codepoint validity (PVALID,
CONTEXTJ, CONTEXTO classes from :rfc:`5892`), and the Bidi Rule
(:func:`check_bidi`).
:param label: The label to validate. ``bytes`` or ``bytearray`` input
is decoded as UTF-8 first.
:raises IDNAError: If the label is empty or fails a structural rule.
:raises InvalidCodepoint: If the label contains a DISALLOWED or
UNASSIGNED codepoint.
:raises InvalidCodepointContext: If a CONTEXTJ or CONTEXTO codepoint
is not valid in its context.
:raises IDNABidiError: If the Bidi Rule is violated.
"""
if len(label) > _max_input_length:
raise IDNAError("Label too long")
if isinstance(label, (bytes, bytearray)):
label = label.decode("utf-8")
if len(label) == 0:
raise IDNAError("Empty Label")
# Reject on domain length rather than label length so support some UTS 46
# use cases, still reducing processing of label contextual rules
if not valid_string_length(label, trailing_dot=True):
raise IDNAError("Label too long")
check_nfc(label)
check_hyphen_ok(label)
check_initial_combiner(label)
for pos, cp in enumerate(label):
cp_value = ord(cp)
if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]):
continue
if intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]):
try:
if not valid_contextj(label, pos):
raise InvalidCodepointContext(f"Joiner {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}")View on GitHub (pinned to d7d0d0a394)
Solutions
- Reject empty labels at the input boundary before IDNA.
- Collapse consecutive dots / strip trailing dots before splitting: re.sub(r'\.+', '.', s).rstrip('.').
- Treat empty input as an application-level error with a clear message rather than passing to idna.
Example fix
# before
parts = 'a..b'.split('.')
for p in parts: idna.check_label(p) # '' -> Empty Label
# after
parts = [p for p in 'a..b'.split('.') if p]
for p in parts: idna.check_label(p) Defensive patterns
Strategy: validation
Validate before calling
def reject_empty_labels(domain: str) -> list:
parts = [p for p in domain.split('.') if p != '']
if not parts:
raise ValueError('empty domain')
return parts Type guard
def has_no_empty_labels(domain: str) -> bool:
return all(p != '' for p in domain.split('.')) and domain != '' Try / catch
from idna import IDNAError
try:
idna.check_label(label)
except IDNAError as e:
if 'Empty' in str(e):
# skip this label rather than aborting the whole domain
return None
raise Prevention
- Collapse consecutive dots and strip trailing dots before splitting: re.sub(r'\.+', '.', s).rstrip('.').
- Filter out empty segments after split.
- Reject empty input at the application boundary with a clear error.
When it happens
Trigger: Calling check_label/alabel with '' or b''; also reached when a domain like 'a..b' is split and an interior empty segment is passed to alabel (encode() guards this earlier with 'Empty label', but direct check_label callers see 'Empty Label').
Common situations: Double dots in user-typed hostnames ('example..com'); trailing-dot handling that leaves an empty segment; concatenation producing 'a.'+''; sanitizers that strip everything leaving ''.
Related errors
- Label too long
- Label has disallowed hyphens in 3rd and 4th position
- Label must not start or end with a hyphen
- {name} has an invalid wheel, {e}
- multiple .dist-info directories found: {}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/0b1d8fd1533477b7.json.
Report an issue: GitHub.