{"id":"971113d3279ebe6f","repo":"pypa/pip","slug":"invalid-ascii-in-a-label","errorCode":null,"errorMessage":"Invalid ASCII in A-label","messagePattern":"Invalid ASCII in A-label","errorType":"validation","errorClass":"IDNAError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/idna/core.py","lineNumber":617,"sourceCode":"    :param uts46: If ``True``, apply UTS #46 mapping before decoding.\n    :param std3_rules: Forwarded to :func:`uts46_remap` when ``uts46`` is\n        ``True``.\n    :param display: If ``True``, any ``xn--`` label that fails IDNA\n        validation is passed through unchanged (lowercased) rather than\n        aborting the whole call. Intended for \"decode for display\"\n        consumers (e.g. URL libraries, HTTP clients) that want to show\n        the user the label as it appears on the wire when it cannot be\n        rendered as Unicode. Matches the per-label recovery prescribed\n        by UTS #46 §4 and the WHATWG URL \"domain to Unicode\" algorithm.\n    :returns: The decoded domain as a Unicode string.\n    :raises IDNAError: If the input is not valid ASCII, contains an\n        invalid label, or is empty.\n    \"\"\"\n    if not isinstance(s, str):\n        try:\n            s = str(s, \"ascii\")\n        except (UnicodeDecodeError, TypeError) as err:\n            raise IDNAError(\"Invalid ASCII in A-label\") from err\n    if len(s) > _max_input_length:\n        raise IDNAError(\"Domain too long\")\n    if uts46:\n        s = uts46_remap(s, std3_rules, False)\n    # 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    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 not labels[-1]:\n        del labels[-1]\n        trailing_dot = True\n    for label in labels:\n        try:","sourceCodeStart":599,"sourceCodeEnd":635,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/idna/core.py#L599-L635","documentation":"Raised by decode() (core.py:617) when the input is not a str and cannot be decoded as ASCII — i.e. the caller passed bytes/bytearray containing non-ASCII octets. decode() expects a str (or pure-ASCII bytes); non-ASCII bytes are not valid in an A-label, which is by definition ASCII, so the UnicodeDecodeError/TypeError is wrapped as IDNAError('Invalid ASCII in A-label').","triggerScenarios":"Calling idna.decode() with bytes that contain non-ASCII octets, e.g. idna.decode(b'xn--mnchen-3ya\\xc3\\xa4'), or passing a bytes value read from a binary source without first decoding it. Any non-ASCII byte is impossible in a well-formed A-label.","commonSituations":"Reading A-labels from binary sources (files opened 'rb', sockets, DB BLOBs), mixing str/bytes across an API boundary, or framework code passing raw request bytes into a URL/IDNA decode path.","solutions":["Decode the bytes as ASCII (A-labels are ASCII by definition): idna.decode(s.decode('ascii')).","If the source is UTF-8 text, decode it to str first and validate it is ASCII-range before calling decode().","Make the caller-side type explicit: accept only str at the boundary and coerce/validate early."],"exampleFix":"// before\nidna.decode(b'xn--mnchen-3ya\\xc3\\xa4')  # non-ASCII byte -> IDNAError\n\n// after\nidna.decode('xn--mnchen-3ya')           # pass an ASCII str\n# or, coming from bytes that should be ASCII:\nidna.decode(raw.decode('ascii'))","handlingStrategy":"type-guard","validationCode":"def to_ascii_domain(s):\n    if isinstance(s, (bytes, bytearray)):\n        try:\n            return s.decode('ascii')\n        except UnicodeDecodeError as err:\n            raise ValueError('A-label must be pure ASCII') from err\n    if not isinstance(s, str):\n        raise TypeError(f'expected str or bytes, got {type(s).__name__}')\n    return s\n\ndecoded = idna.decode(to_ascii_domain(raw))","typeGuard":"def is_str(s) -> bool:\n    return isinstance(s, str)","tryCatchPattern":"import idna\n\ntry:\n    decoded = idna.decode(raw)\nexcept idna.IDNAError as err:\n    if 'Invalid ASCII' in str(err):\n        # raw was non-ASCII bytes; decode the source as UTF-8 and retry if appropriate\n        decoded = idna.decode(raw.decode('utf-8'))\n    else:\n        raise","preventionTips":["Decode bytes to str at the boundary before passing to idna.decode().","Validate bytes are pure-ASCII before treating them as an A-label.","Type-annotate domain parameters as str and enforce with a runtime check."],"tags":["idna","domain","types","bytes","ascii"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}