pypa/pip · error · IDNABidiError

First codepoint in label {label!r} must be directionality L,

Error message

First codepoint in label {label!r} must be directionality L, R or AL

What it means

IDNABidiError from check_bidi Bidi Rule 1: the first codepoint of a label under Bidi scrutiny must have bidirectional category L (left-to-right), R, or AL (Arabic letter). Any other leading category (e.g. ET, ON, NSM, EN) is rejected because RFC 5893 requires a strong directional start.

Source

Thrown at src/pip/_vendor/idna/core.py:136

    bidi_label = False
    for idx, cp in enumerate(label, 1):
        direction = unicodedata.bidirectional(cp)
        if direction == "":
            # String likely comes from a newer version of Unicode
            raise IDNABidiError(f"Unknown directionality in label {label!r} at position {idx}")
        if direction in _bidi_rtl_categories:
            bidi_label = True
    if not bidi_label and not check_ltr:
        return True

    # Bidi rule 1
    direction = unicodedata.bidirectional(label[0])
    if direction in _bidi_rtl_first:
        rtl = True
    elif direction == "L":
        rtl = False
    else:
        raise IDNABidiError(f"First codepoint in label {label!r} must be directionality L, R or AL")

    valid_ending = False
    number_type: Optional[str] = None
    for idx, cp in enumerate(label, 1):
        direction = unicodedata.bidirectional(cp)

        if rtl:
            # Bidi rule 2
            if direction not in _bidi_rtl_allowed:
                raise IDNABidiError(f"Invalid direction for codepoint at position {idx} in a right-to-left label")
            # Bidi rule 3
            if direction in _bidi_rtl_valid_ending:
                valid_ending = True
            elif direction != "NSM":
                valid_ending = False
            # Bidi rule 4
            if direction in _bidi_rtl_numeric:
                if not number_type:

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Restructure the label so a strong L/R/AL letter is first (move digits/punctuation after the first letter).
  2. Strip leading punctuation/digits from user input before IDNA encoding.
  3. Split into multiple labels so each starts with a valid directional character.

Example fix

# before
idna.encode('1-العرب')  # First codepoint must be L, R or AL

# after
idna.encode('العرب-1')
Defensive patterns

Strategy: validation

Validate before calling

import unicodedata
_VALID_FIRST_BIDI = {'L', 'R', 'AL'}
def has_valid_first_bidi(label: str) -> bool:
    return bool(label) and unicodedata.bidirectional(label[0]) in _VALID_FIRST_BIDI

Type guard

import unicodedata
def is_strong_directional_start(label: str) -> bool:
    return bool(label) and unicodedata.bidirectional(label[0]) in {'L', 'R', 'AL'}

Try / catch

from idna import IDNABidiError
try:
    idna.check_bidi(label)
except IDNABidiError as e:
    if 'First codepoint' in str(e):
        label = label.lstrip('0123456789-,.;')  # trim weak leading chars
    else:
        raise

Prevention

When it happens

Trigger: A label starting with a digit, punctuation, combining mark, or European number while the label also contains RTL characters (forcing the Bidi check). Example: '1اabc' or ',بtest'.

Common situations: Auto-generated slugs that prefix Arabic/Hebrew words with ASCII digits or punctuation; user handles or subdomains beginning with a number followed by RTL text; data migration that prepended a marker character to existing IDN names.

Related errors


AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04). Data as JSON: /data/errors/f82a6562df4dcfe1.json. Report an issue: GitHub.