{"id":"708049a4d4a6905d","repo":"pypa/pip","slug":"invalid-a-label","errorCode":null,"errorMessage":"Invalid A-label","messagePattern":"Invalid A-label","errorType":"validation","errorClass":"IDNAError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/idna/core.py","lineNumber":449,"sourceCode":"            return label\n    else:\n        label_bytes = bytes(label)\n\n    label_bytes = label_bytes.lower()\n    if label_bytes.startswith(_alabel_prefix):\n        label_bytes = label_bytes[len(_alabel_prefix) :]\n        if not label_bytes:\n            raise IDNAError(\"Malformed A-label, no Punycode eligible content found\")\n        if label_bytes.endswith(b\"-\"):\n            raise IDNAError(\"A-label must not end with a hyphen\")\n    else:\n        check_label(label_bytes)\n        return label_bytes.decode(\"ascii\")\n\n    try:\n        label = label_bytes.decode(\"punycode\")\n    except UnicodeError as err:\n        raise IDNAError(\"Invalid A-label\") from err\n    check_label(label)\n    return label\n\n\ndef uts46_remap(domain: str, std3_rules: bool = True, transitional: bool = False) -> str:\n    \"\"\"Apply the UTS #46 character mapping to a domain string.\n\n    Implements the mapping table from `UTS #46 §4\n    <https://www.unicode.org/reports/tr46/>`_: each character is kept,\n    replaced, or rejected based on its status (``V``, ``M``, ``D``, ``3``,\n    ``I``). The result is returned in Normalisation Form C.\n\n    :param domain: The full domain name to remap.\n    :param std3_rules: If ``True``, apply the stricter STD3 ASCII rules\n        (status ``3`` codepoints raise instead of being kept or mapped).\n    :param transitional: If ``True``, use transitional processing (status\n        ``D`` codepoints are mapped instead of kept). Transitional\n        processing has been removed from UTS #46 and this option is","sourceCodeStart":431,"sourceCodeEnd":467,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/idna/core.py#L431-L467","documentation":"Raised by ulabel() (core.py:449) when the bytes after 'xn--' are present and not trailing-hyphen but fail to Punycode-decode — Python's codecs.ascii/punycode decode raises UnicodeError, which is wrapped as IDNAError('Invalid A-label'). The label looks like an A-label structurally but its payload is not valid Punycode.","triggerScenarios":"Calling idna.ulabel() or idna.decode() with a label that has the 'xn--' prefix and a non-empty, non-trailing-hyphen payload that nonetheless is not valid Punycode — e.g. 'xn--!!', 'xn--zzz invalid', or a label where the Punycode extension integers are out of range. Also hit when an unrelated string happens to start with 'xn--'.","commonSituations":"Corrupted DNS responses, hand-typed or templated ACE labels with invalid characters, adversarial/obfuscated input probing the decoder, or misrouted opaque tokens that coincidentally begin with 'xn--'.","solutions":["Treat the value as untrusted and reject it; a non-decodable A-label cannot be turned into a meaningful U-label.","If you only need a displayable form, call decode(domain, display=True) which passes the bad label through lowercased instead of raising.","Regenerate the A-label from a known-good Unicode source via idna.alabel() rather than trusting externally-supplied 'xn--' strings."],"exampleFix":"// before\nidna.decode('xn--!!invalid!!.example.com')  # payload not Punycode\n\n// after\nidna.decode('xn--!!invalid!!.example.com', display=True)  # pass-through\n# or reject upstream:\ntry:\n    idna.ulabel(label)\nexcept idna.IDNAError:\n    label = label.lower()  # graceful fallback for display","handlingStrategy":"try-catch","validationCode":"def looks_like_valid_punycode(label) -> bool:\n    # cheap heuristic: try to round-trip through punycode\n    s = label.decode('ascii') if isinstance(label, (bytes, bytearray)) else label\n    if not s.lower().startswith('xn--'):\n        return True\n    payload = s[4:]\n    try:\n        payload.encode('ascii').decode('punycode')\n        return True\n    except (UnicodeError, ValueError):\n        return False","typeGuard":"import re\nACE_RE = re.compile(r'^xn--[A-Za-z0-9](?:[A-Za-z0-9-]*[A-Za-z0-9])?$', re.IGNORECASE)\n\ndef is_plausible_ace_label(s) -> bool:\n    return isinstance(s, str) and bool(ACE_RE.match(s))","tryCatchPattern":"import idna\n\ntry:\n    decoded = idna.decode(domain)\nexcept idna.IDNAError as err:\n    if 'Invalid A-label' in str(err):\n        # payload not decodable; display-only fallback or reject\n        decoded = idna.decode(domain, display=True)\n    else:\n        raise","preventionTips":["Treat externally-supplied 'xn--' labels as untrusted; never assume they decode.","Use decode(domain, display=True) in URL/HTTP display paths so bad labels pass through.","Round-trip-validate stored A-labels (alabel -> ulabel -> alabel) at ingest time."],"tags":["idna","domain","punycode","a-label","malformed"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}