{"id":"48bc01cc13e178d4","repo":"pypa/pip","slug":"empty-label-48bc01","errorCode":null,"errorMessage":"Empty label","messagePattern":"Empty label","errorType":"validation","errorClass":"IDNAError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/idna/core.py","lineNumber":573,"sourceCode":"    # Reject inputs that exceed the maximum DNS domain length up-front\n    # to avoid expensive computation on long inputs.\n    if not valid_string_length(s, trailing_dot=True):\n        raise IDNAError(\"Domain too long\")\n\n    trailing_dot = False\n    result = []\n    labels = s.split(\".\") if strict else _unicode_dots_re.split(s)\n    if not labels or labels == [\"\"]:\n        raise IDNAError(\"Empty domain\")\n    if labels[-1] == \"\":\n        del labels[-1]\n        trailing_dot = True\n    for label in labels:\n        s = alabel(label)\n        if s:\n            result.append(s)\n        else:\n            raise IDNAError(\"Empty label\")\n    if trailing_dot:\n        result.append(b\"\")\n    s = b\".\".join(result)\n    if not valid_string_length(s, trailing_dot):\n        raise IDNAError(\"Domain too long\")\n    return s\n\n\ndef decode(\n    s: Union[str, bytes, bytearray],\n    strict: bool = False,\n    uts46: bool = False,\n    std3_rules: bool = False,\n    display: bool = False,\n) -> str:\n    \"\"\"Decode an A-label-encoded domain name back to Unicode.\n\n    Splits the input on label separators (see :func:`encode` for the","sourceCodeStart":555,"sourceCodeEnd":591,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/idna/core.py#L555-L591","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Normalise the domain by collapsing consecutive dots and stripping leading/trailing dots before encoding.","Validate that every dot-separated segment is non-empty: all(labels) and all(l != '' for l in labels).","Fix the templating/construction bug that produced the empty middle segment."],"exampleFix":"// before\nidna.encode(f'{a}.{middle}.{b}')  # middle == '' -> 'a..b'\n\n// after\nlabels = [l for l in domain.split('.') if l]\nif not labels:\n    raise ValueError('empty domain')\nidna.encode('.'.join(labels))","handlingStrategy":"validation","validationCode":"def has_no_empty_labels(domain: str) -> bool:\n    labels = domain.split('.')\n    # allow a single trailing empty label (trailing dot)\n    if labels and labels[-1] == '':\n        labels = labels[:-1]\n    return bool(labels) and all(l != '' for l in labels)\n\nif not has_no_empty_labels(domain):\n    raise ValueError(f'domain {domain!r} has empty labels (consecutive dots)')\nencoded = idna.encode(domain)","typeGuard":"def is_clean_domain(s) -> bool:\n    if not isinstance(s, str) or not s:\n        return False\n    return '..' not in s and not s.startswith('.')","tryCatchPattern":"import idna\n\ntry:\n    encoded = idna.encode(domain)\nexcept idna.IDNAError as err:\n    if 'Empty label' in str(err):\n        # collapse consecutive dots and retry\n        import re\n        cleaned = re.sub(r'\\.+', '.', domain).strip('.')\n        encoded = idna.encode(cleaned)\n    else:\n        raise","preventionTips":["Normalise domains: collapse consecutive dots and strip leading/trailing dots before encoding.","Validate every dot-separated segment is non-empty before encoding.","Guard templating (f'{a}.{b}.{c}') against empty middle components."],"tags":["idna","domain","empty","input-validation"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}