{"id":"58a455dc7a44fcb9","repo":"pypa/pip","slug":"can-not-mix-numeral-types-in-a-right-to-left-label","errorCode":null,"errorMessage":"Can not mix numeral types in a right-to-left label","messagePattern":"Can not mix numeral types in a right-to-left label","errorType":"validation","errorClass":"IDNABidiError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/idna/core.py","lineNumber":157,"sourceCode":"    number_type: Optional[str] = None\n    for idx, cp in enumerate(label, 1):\n        direction = unicodedata.bidirectional(cp)\n\n        if rtl:\n            # Bidi rule 2\n            if direction not in _bidi_rtl_allowed:\n                raise IDNABidiError(f\"Invalid direction for codepoint at position {idx} in a right-to-left label\")\n            # Bidi rule 3\n            if direction in _bidi_rtl_valid_ending:\n                valid_ending = True\n            elif direction != \"NSM\":\n                valid_ending = False\n            # Bidi rule 4\n            if direction in _bidi_rtl_numeric:\n                if not number_type:\n                    number_type = direction\n                elif number_type != direction:\n                    raise IDNABidiError(\"Can not mix numeral types in a right-to-left label\")\n        else:\n            # Bidi rule 5\n            if direction not in _bidi_ltr_allowed:\n                raise IDNABidiError(f\"Invalid direction for codepoint at position {idx} in a left-to-right label\")\n            # Bidi rule 6\n            if direction in _bidi_ltr_valid_ending:\n                valid_ending = True\n            elif direction != \"NSM\":\n                valid_ending = False\n\n    if not valid_ending:\n        raise IDNABidiError(\"Label ends with illegal codepoint directionality\")\n\n    return True\n\n\ndef check_initial_combiner(label: str) -> bool:\n    \"\"\"Reject labels that begin with a combining mark.","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/idna/core.py#L139-L175","documentation":"IDNABidiError from check_bidi Bidi Rule 4: a right-to-left label may contain European Number (EN) or Arabic-Number (AN) codepoints, but not both. Mixing the two numeral systems (e.g. Western digits 0-9 with Arabic-Indic digits) in one RTL label is forbidden to keep rendering unambiguous.","triggerScenarios":"A label containing both ASCII digits (0-9, category EN) and Arabic-Indic digits (٠-٩, category AN), or both EN and Extended Arabic-Indic digits, within an RTL (R/AL) label. Example: 'اا1٢' mixes '1' (EN) and '٢' (AN).","commonSituations":"Phone-number or product-code subdomains that combine ASCII digits with Arabic-Indic digits; data feeds merging localized numeric fragments; OCR or transliteration pipelines that normalize some but not all digits.","solutions":["Normalize all digits in the label to a single numeral system (prefer ASCII EN, or fully Arabic-Indic AN) before encoding.","Use str.translate with a digit-unification mapping table.","Validate with a regex that the label contains only one digit class."],"exampleFix":"# before\nidna.encode('اا1٢٣')  # Can not mix numeral types\n\n# after\narabic_indic = {ord(a): ord(b) for a, b in zip('٠١٢٣٤٥٦٧٨٩', '0123456789')}\nidna.encode('اا1٢٣'.translate(arabic_indic))","handlingStrategy":"validation","validationCode":"import unicodedata\ndef unify_digits(label: str) -> str:\n    # collapse Arabic-Indic and Extended Arabic-Indic digits to ASCII\n    m = {ord(a): ord(b) for a, b in zip('٠١٢٣٤٥٦٧٨٩۰۱۲۳۴۵۶۷۸۹', '0123456789'*2)}\n    return label.translate(m)\ndef has_single_numeral_type(label: str) -> bool:\n    cats = {unicodedata.bidirectional(c) for c in label}\n    return not ({'EN','AN'} <= cats and len({'EN','AN'} & cats) > 1)","typeGuard":"import unicodedata\ndef uses_single_numeral(label: str) -> bool:\n    found = {unicodedata.bidirectional(c) for c in label}\n    return not ({'EN','AN'} <= cats := found)","tryCatchPattern":"from idna import IDNABidiError\ntry:\n    idna.encode(label)\nexcept IDNABidiError as e:\n    if 'mix numeral' in str(e):\n        label = unify_digits(label)  # retry with unified digits\n    else:\n        raise","preventionTips":["Normalize all digits in a label to one system (ASCII EN or fully AN) before encoding.","For numeric subdomains, prefer ASCII digits.","Validate with a regex that only one digit class appears."],"tags":["idna","bidi","rfc5893","numerals","pip"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}