pypa/pip · error · InvalidCodepointContext

Codepoint {_unot(cp_value)} not allowed at position {pos + 1

Error message

Codepoint {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}

What it means

Raised by check_label (core.py:370) during IDNA 2008 validation when a CONTEXTO-class codepoint appears in a context not permitted by RFC 5892 Appendix A. CONTEXTO codepoints (MIDDLE DOT U+00B7, Greek lower numeral sign U+0375, Hebrew geresh/gereshayim U+05F3/U+05F4, Katakana middle dot U+30FB, Arabic-Indic digits U+0660-0669, Extended Arabic-Indic digits U+06F0-06F9) are only legal alongside specific scripts or characters; if valid_contexto() returns False the label is rejected with InvalidCodepointContext. The message names the codepoint (U+XXXX) and its 1-based position so the offending character is identifiable.

Source

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

    check_nfc(label)
    check_hyphen_ok(label)
    check_initial_combiner(label)

    for pos, cp in enumerate(label):
        cp_value = ord(cp)
        if intranges_contain(cp_value, idnadata.codepoint_classes["PVALID"]):
            continue
        if intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTJ"]):
            try:
                if not valid_contextj(label, pos):
                    raise InvalidCodepointContext(f"Joiner {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}")
            except ValueError as err:
                raise IDNAError(
                    f"Unknown codepoint adjacent to joiner {_unot(cp_value)} at position {pos + 1} in {label!r}"
                ) from err
        elif intranges_contain(cp_value, idnadata.codepoint_classes["CONTEXTO"]):
            if not valid_contexto(label, pos):
                raise InvalidCodepointContext(f"Codepoint {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}")
        else:
            raise InvalidCodepoint(f"Codepoint {_unot(cp_value)} at position {pos + 1} of {label!r} not allowed")

    check_bidi(label)


def alabel(label: str) -> bytes:
    """Convert a single U-label into its A-label form.

    The result is the ASCII-Compatible Encoding (ACE) form per :rfc:`5891`
    §4: the label is validated, Punycode-encoded, and prefixed with
    ``xn--``. Pure ASCII labels that are already valid IDNA labels are
    returned unchanged (as :class:`bytes`).

    :param label: The label to convert, as a Unicode string.
    :returns: The A-label as ASCII-encoded :class:`bytes`.
    :raises IDNAError: If the label is invalid or the resulting A-label
        exceeds 63 octets.

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Identify the codepoint from the U+XXXX token in the message and remove or replace it (typically with an ASCII hyphen or nothing) before calling encode().
  2. If the punctuation is intentional, restructure the surrounding characters to satisfy the rule, e.g. ensure a U+00B7 is flanked by 'l' on both sides, or drop the Arabic-Indic digit block that conflicts.
  3. Pre-process the domain with idna.uts46_remap(domain, std3_rules=False) or call encode(domain, uts46=True) so the UTS #46 mapping table normalises or rejects the character consistently.

Example fix

// before
idna.encode('exa·mple.com')  # middle dot not between two 'l'

// after
idna.encode('exa-mple.com')   # use ASCII hyphen
# or satisfy the Catalan rule:
idna.encode('pa·l·l.com')  # 'l·l' is valid CONTEXTO
Defensive patterns

Strategy: validation

Validate before calling

import idna

def is_valid_idna_label(label: str) -> bool:
    try:
        idna.check_label(label)
        return True
    except idna.IDNAError:
        return False

# pre-check before encode/alabel
if not is_valid_idna_label(label):
    raise ValueError(f'label {label!r} fails IDNA validation')
encoded = idna.alabel(label)

Type guard

def is_idna_label_safe(s) -> bool:
    return isinstance(s, str) and len(s) <= 63 and not s.startswith('-') and not s.endswith('-')

Try / catch

import idna

try:
    encoded = idna.encode(domain)
except idna.InvalidCodepointContext as err:
    # CONTEXTO codepoint in illegal context
    raise ValueError(f'invalid domain {domain!r}: {err}') from err

Prevention

When it happens

Trigger: Calling idna.encode(), idna.alabel(), or idna.check_label() with a Unicode label whose CONTEXTO codepoint fails its rule: a U+00B7 not sandwiched between two lowercase 'l' (Catalan l·l), a Katakana middle dot with no Hiragana/Katakana/Han character in the label, Arabic-Indic and Extended Arabic-Indic digits mixed in the same label, or a Hebrew U+05F3/U+05F4 not preceded by a Hebrew character.

Common situations: Hostnames pasted from word processors that substitute a real middle dot for a hyphen, transliterated/multilingual test fixtures mixing Latin punctuation with CJK or Arabic content, user-generated subdomains that accidentally include numeral-sign punctuation, and data migrated from systems using IDNA 2003 which did not enforce these contextual rules.

Related errors


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