{"id":"238c5e7924750e9d","repo":"pypa/pip","slug":"joiner-unot-cp-value-not-allowed-at-position","errorCode":null,"errorMessage":"Joiner {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}","messagePattern":"Joiner (.+?) not allowed at position (.+?) in (.+?)","errorType":"validation","errorClass":"InvalidCodepointContext","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/idna/core.py","lineNumber":363,"sourceCode":"        raise IDNAError(\"Empty Label\")\n\n    # Reject on domain length rather than label length so support some UTS 46\n    # use cases, still reducing processing of label contextual rules\n    if not valid_string_length(label, trailing_dot=True):\n        raise IDNAError(\"Label too long\")\n\n    check_nfc(label)\n    check_hyphen_ok(label)\n    check_initial_combiner(label)\n\n    for pos, cp in enumerate(label):\n        cp_value = ord(cp)\n        if intranges_contain(cp_value, idnadata.codepoint_classes[\"PVALID\"]):\n            continue\n        if intranges_contain(cp_value, idnadata.codepoint_classes[\"CONTEXTJ\"]):\n            try:\n                if not valid_contextj(label, pos):\n                    raise InvalidCodepointContext(f\"Joiner {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}\")\n            except ValueError as err:\n                raise IDNAError(\n                    f\"Unknown codepoint adjacent to joiner {_unot(cp_value)} at position {pos + 1} in {label!r}\"\n                ) from err\n        elif intranges_contain(cp_value, idnadata.codepoint_classes[\"CONTEXTO\"]):\n            if not valid_contexto(label, pos):\n                raise InvalidCodepointContext(f\"Codepoint {_unot(cp_value)} not allowed at position {pos + 1} in {label!r}\")\n        else:\n            raise InvalidCodepoint(f\"Codepoint {_unot(cp_value)} at position {pos + 1} of {label!r} not allowed\")\n\n    check_bidi(label)\n\n\ndef alabel(label: str) -> bytes:\n    \"\"\"Convert a single U-label into its A-label form.\n\n    The result is the ASCII-Compatible Encoding (ACE) form per :rfc:`5891`\n    §4: the label is validated, Punycode-encoded, and prefixed with","sourceCodeStart":345,"sourceCodeEnd":381,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/idna/core.py#L345-L381","documentation":"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.","triggerScenarios":"A label containing U+200C or U+200D not surrounded by the required Indic/Arabic joining context, e.g. 'ab‌cd' (ZWNJ with no Virama and no L/D-R/D joiner pairing). The position reported is 1-based.","commonSituations":"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.","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)."],"exampleFix":"# before\nidna.encode('ab‌cd')  # Joiner U+200C not allowed at position 3\n\n# after\nclean = label.replace('‌', '').replace('‍', '')\nidna.encode(clean)","handlingStrategy":"validation","validationCode":"def strip_stray_joiners(label: str) -> str:\n    # only keep U+200C/U+200D when surrounded by legitimate Indic/Arabic context\n    return label.replace('\\u200c', '').replace('\\u200d', '')\ndef has_safe_contextj(label: str) -> bool:\n    from idna.core import valid_contextj\n    return all(valid_contextj(label, i) for i, c in enumerate(label) if c in '\\u200c\\u200d')","typeGuard":"def has_no_unsupported_joiners(label: str) -> bool:\n    # conservative guard: joiners only valid in known Indic/Arabic contexts\n    return all(c not in '\\u200c\\u200d' for c in label) or label.isidentifier() is False","tryCatchPattern":"from idna import InvalidCodepointContext, IDNAError\ntry:\n    idna.encode(label)\nexcept InvalidCodepointContext as e:\n    if 'Joiner' in str(e):\n        label = label.replace('\\u200c', '').replace('\\u200d', '')  # retry without stray joiners\n    else:\n        raise","preventionTips":["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."],"tags":["idna","contextj","rfc5892","zero-width","unicode","pip"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}