pypa/pip · error · IDNAError

Unknown codepoint adjacent to joiner {_unot(cp_value)} at po

Error message

Unknown codepoint adjacent to joiner {_unot(cp_value)} at position {pos + 1} in {label!r}

What it means

IDNAError raised by check_label when valid_contextj itself raises ValueError while inspecting the neighbors of a CONTEXTJ joiner. _combining_class throws ValueError('Unknown character in unicodedata') when a codepoint adjacent to the joiner has no name in the running Python's unicodedata database — i.e. it belongs to a newer Unicode version than the runtime knows about.

Source

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

    # Reject on domain length rather than label length so support some UTS 46
    # use cases, still reducing processing of label contextual rules
    if not valid_string_length(label, trailing_dot=True):
        raise IDNAError("Label too long")

    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`).

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Upgrade Python to a release built against a newer Unicode version (Unicode tables are baked in at CPython build time).
  2. Pre-filter labels to reject codepoints the runtime cannot name: skip if not unicodedata.name(chr(cp), None).
  3. Strip the problematic adjacent character or replace the joiner context with a runtime-supported equivalent.

Example fix

# before
# label has U+200D + a newer-Unicode Virama char
idna.encode(label)  # Unknown codepoint adjacent to joiner

# after
# upgrade runtime, or pre-filter:
if any(unicodedata.name(c, None) is None for c in label):
    raise ValueError('label uses unsupported codepoints for this runtime')
idna.encode(label)
Defensive patterns

Strategy: validation

Validate before calling

import unicodedata
def runtime_knows_all_chars(label: str) -> bool:
    return all(unicodedata.name(c, None) is not None for c in label)

Type guard

import unicodedata
def is_label_runtime_supported(label: str) -> bool:
    return all(unicodedata.name(c, None) is not None for c in label)

Try / catch

from idna import IDNAError
try:
    idna.encode(label)
except IDNAError as e:
    if 'Unknown codepoint adjacent to joiner' in str(e):
        raise ValueError('runtime Unicode tables too old for this label; upgrade Python') from e
    raise

Prevention

When it happens

Trigger: A label containing U+200C or U+200D plus an adjacent codepoint introduced in a Unicode version newer than the running CPython's bundled tables (e.g. a new Indic Virama or joining character on an older Python). The original ValueError is chained via 'from err'.

Common situations: Running on an older Python (3.6/3.7) processing modern Indic/Arabic domain data; CI on a distro Python with stale Unicode tables; apps on frozen runtimes receiving user input with recent script additions.

Related errors


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