pypa/pip · error · IDNABidiError
Label ends with illegal codepoint directionality
Error message
Label ends with illegal codepoint directionality
What it means
IDNABidiError from check_bidi: after scanning all codepoints, the label must end with a codepoint whose bidi category is a valid ending category (R, AL, EN, AN for RTL; L, EN for LTR). Ending with a separator/neutral like ES, CS, ON (e.g. a hyphen, dot-like char, or punctuation) after the last strong character is rejected.
Source
Thrown at src/pip/_vendor/idna/core.py:169
valid_ending = False
# Bidi rule 4
if direction in _bidi_rtl_numeric:
if not number_type:
number_type = direction
elif number_type != direction:
raise IDNABidiError("Can not mix numeral types in a right-to-left label")
else:
# Bidi rule 5
if direction not in _bidi_ltr_allowed:
raise IDNABidiError(f"Invalid direction for codepoint at position {idx} in a left-to-right label")
# Bidi rule 6
if direction in _bidi_ltr_valid_ending:
valid_ending = True
elif direction != "NSM":
valid_ending = False
if not valid_ending:
raise IDNABidiError("Label ends with illegal codepoint directionality")
return True
def check_initial_combiner(label: str) -> bool:
"""Reject labels that begin with a combining mark.
Per :rfc:`5891` §4.2.3.2 a label must not start with a character of
Unicode general category ``M`` (Mark).
:param label: The label to check.
:returns: ``True`` if the first character is not a combining mark.
:raises IDNAError: If the label begins with a combining character.
"""
if unicodedata.category(label[0])[0] == "M":
raise IDNAError("Label begins with an illegal combining character")
return True
View on GitHub (pinned to d7d0d0a394)
Solutions
- Trim trailing separators, hyphens, and neutral/punctuation characters from the label before encoding.
- Ensure the last character is a letter (L/R/AL) or number (EN/AN).
- Review your slugify/sanitizer to forbid trailing non-alphanumerics.
Example fix
# before
idna.encode('العرب-') # Label ends with illegal codepoint directionality
# after
idna.encode('العرب') Defensive patterns
Strategy: validation
Validate before calling
import unicodedata
def has_valid_bidi_ending(label: str) -> bool:
if not label:
return False
d = unicodedata.bidirectional(label[-1])
return d in {'L','R','AL','EN','AN'} Type guard
import unicodedata
def ends_with_strong_or_number(label: str) -> bool:
return bool(label) and unicodedata.bidirectional(label[-1]) in {'L','R','AL','EN','AN'} Try / catch
from idna import IDNABidiError
try:
idna.encode(label)
except IDNABidiError as e:
if 'ends with illegal' in str(e):
label = label.rstrip('-.,;:_/') # trim trailing neutrals
else:
raise Prevention
- Trim trailing separators, hyphens, and neutral/punctuation characters before encoding.
- Ensure the last character is a letter or number.
- Slugify routines must strip trailing non-alphanumerics.
When it happens
Trigger: A label whose final character is punctuation, a separator, or a symbol rather than a letter or number, e.g. 'الع-' (Arabic label ending in hyphen) or 'abc.' (after the trailing-dot split leaves a punctuation tail).
Common situations: Hyphens appended by a slugify routine that did not trim the tail; trailing combining marks stripped leaving a NSM-neutral final char; concatenation that leaves a separator dangling at the end.
Related errors
- First codepoint in label {label!r} must be directionality L,
- Invalid direction for codepoint at position {idx} in a right
- Can not mix numeral types in a right-to-left label
- Invalid direction for codepoint at position {idx} in a left-
- Unknown directionality in label {label!r} at position {idx}
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/620eb8f59abf491e.json.
Report an issue: GitHub.