pypa/pip · error · InvalidCodepointContext
Joiner {_unot(cp_value)} not allowed at position {pos + 1} i
Error message
Joiner {_unot(cp_value)} not allowed at position {pos + 1} in {label!r} What it means
InvalidCodepointContext raised by check_label when valid_contextj returns False for a CONTEXTJ codepoint (U+200C ZERO WIDTH NON-JOINER or U+200D ZERO WIDTH JOINER). Per RFC 5892 Appendix A, these joiners are only legal in specific contexts: ZWJ requires a preceding Virama-combining-class character; ZWNJ requires either a preceding Virama or a joining-type L/D before and R/D after.
Source
Thrown at src/pip/_vendor/idna/core.py:363
raise IDNAError("Empty Label")
# 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 withView on GitHub (pinned to d7d0d0a394)
Solutions
- Strip CONTEXTJ codepoints (U+200C, U+200D) from labels that are not legitimately Indic/Arabic.
- Restore the required Virama-class or joining-type context around the joiner.
- Validate the label's script before allowing joiners (only Devanagari, Arabic, etc., contexts).
Example fix
# before
idna.encode('abcd') # Joiner U+200C not allowed at position 3
# after
clean = label.replace('', '').replace('', '')
idna.encode(clean) Defensive patterns
Strategy: validation
Validate before calling
def strip_stray_joiners(label: str) -> str:
# only keep U+200C/U+200D when surrounded by legitimate Indic/Arabic context
return label.replace('\u200c', '').replace('\u200d', '')
def has_safe_contextj(label: str) -> bool:
from idna.core import valid_contextj
return all(valid_contextj(label, i) for i, c in enumerate(label) if c in '\u200c\u200d') Type guard
def has_no_unsupported_joiners(label: str) -> bool:
# conservative guard: joiners only valid in known Indic/Arabic contexts
return all(c not in '\u200c\u200d' for c in label) or label.isidentifier() is False Try / catch
from idna import InvalidCodepointContext, IDNAError
try:
idna.encode(label)
except InvalidCodepointContext as e:
if 'Joiner' in str(e):
label = label.replace('\u200c', '').replace('\u200d', '') # retry without stray joiners
else:
raise Prevention
- Strip U+200C/U+200D from any label that is not from a legitimate Indic/Arabic context.
- Sanitize rich-text and emoji copy-paste to remove invisible formatting characters.
- Validate script consistency before allowing joiners.
When it happens
Trigger: A label containing U+200C or U+200D not surrounded by the required Indic/Arabic joining context, e.g. 'abcd' (ZWNJ with no Virama and no L/D-R/D joiner pairing). The position reported is 1-based.
Common situations: Copy-paste that introduces an invisible ZWJ/ZWNJ (often from emoji sequences or rich text); sanitizers that allow general-category Cf formatting chars; concatenation of Indic fragments that drops the base character the joiner depended on.
Related errors
- Unknown codepoint adjacent to joiner {_unot(cp_value)} at po
- Unknown directionality in label {label!r} at position {idx}
- First codepoint in label {label!r} must be directionality L,
- Label begins with an illegal combining character
- Label must be in Normalization Form C
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/238c5e7924750e9d.json.
Report an issue: GitHub.