pypa/pip · error · IDNAError

Empty label

Error message

Empty label

What it means

Raised by encode() (core.py:573) inside the per-label loop when alabel() returns a falsy result for a non-empty-looking label — i.e. encoding produced an empty byte string. In practice this guards against a label that is empty after splitting (consecutive separators producing an empty segment not at the trailing position), which alabel treats as empty.

Source

Thrown at src/pip/_vendor/idna/core.py:573

    # 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 = True
    for label in labels:
        s = alabel(label)
        if s:
            result.append(s)
        else:
            raise IDNAError("Empty label")
    if trailing_dot:
        result.append(b"")
    s = b".".join(result)
    if not valid_string_length(s, trailing_dot):
        raise IDNAError("Domain too long")
    return s


def decode(
    s: Union[str, bytes, bytearray],
    strict: bool = False,
    uts46: bool = False,
    std3_rules: bool = False,
    display: bool = False,
) -> str:
    """Decode an A-label-encoded domain name back to Unicode.

    Splits the input on label separators (see :func:`encode` for the

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Normalise the domain by collapsing consecutive dots and stripping leading/trailing dots before encoding.
  2. Validate that every dot-separated segment is non-empty: all(labels) and all(l != '' for l in labels).
  3. Fix the templating/construction bug that produced the empty middle segment.

Example fix

// before
idna.encode(f'{a}.{middle}.{b}')  # middle == '' -> 'a..b'

// after
labels = [l for l in domain.split('.') if l]
if not labels:
    raise ValueError('empty domain')
idna.encode('.'.join(labels))
Defensive patterns

Strategy: validation

Validate before calling

def has_no_empty_labels(domain: str) -> bool:
    labels = domain.split('.')
    # allow a single trailing empty label (trailing dot)
    if labels and labels[-1] == '':
        labels = labels[:-1]
    return bool(labels) and all(l != '' for l in labels)

if not has_no_empty_labels(domain):
    raise ValueError(f'domain {domain!r} has empty labels (consecutive dots)')
encoded = idna.encode(domain)

Type guard

def is_clean_domain(s) -> bool:
    if not isinstance(s, str) or not s:
        return False
    return '..' not in s and not s.startswith('.')

Try / catch

import idna

try:
    encoded = idna.encode(domain)
except idna.IDNAError as err:
    if 'Empty label' in str(err):
        # collapse consecutive dots and retry
        import re
        cleaned = re.sub(r'\.+', '.', domain).strip('.')
        encoded = idna.encode(cleaned)
    else:
        raise

Prevention

When it happens

Trigger: Calling idna.encode() with a domain containing an internal empty label from consecutive separators, e.g. 'a..b.com', 'a...b.com', or a leading empty segment — anything where split yields '' between two real labels.

Common situations: User-typed domains with double dots, string-joining bugs that emit '..' between parts, templated hostnames with a missing middle component (f'{a}.{b}.{c}' where b is ''), or copy-paste artefacts.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/48bc01cc13e178d4.json. Report an issue: GitHub.