pypa/pip · error · InvalidCodepoint
Codepoint {_unot(code_point)} not allowed at position {pos +
Error message
Codepoint {_unot(code_point)} not allowed at position {pos + 1} in {domain!r} What it means
Raised by uts46_remap() (core.py:505) when a codepoint's UTS #46 status is not V (valid), D (deviation), 3 (disallowed-STD3), or I (ignored) — i.e. it is outright disallowed under UTS #46 and std3/transition handling does not rescue it. The exception class is InvalidCodepoint and the message names the codepoint (U+XXXX) and its position. This is the UTS #46 counterpart of the IDNA 2008 disallowed-codepoint error (261), reached when uts46=True is used.
Source
Thrown at src/pip/_vendor/idna/core.py:505
# 3 is disallowed-STD3 (kept unmapped if std3_rules is off and no mapping).
keep_as_is = (
status == "V" or (status == "D" and not transitional) or (status == "3" and not std3_rules and replacement is None)
)
# M is mapped, 3-with-replacement and transitional D fall through to the
# same replacement output path.
use_replacement = replacement is not None and (
status == "M" or (status == "3" and not std3_rules) or (status == "D" and transitional)
)
if keep_as_is:
output += char
elif use_replacement:
assert replacement is not None # narrowed by use_replacement
output += replacement
elif status == "I":
continue
else:
raise InvalidCodepoint(f"Codepoint {_unot(code_point)} not allowed at position {pos + 1} in {domain!r}")
return unicodedata.normalize("NFC", output)
def encode(
s: Union[str, bytes, bytearray],
strict: bool = False,
uts46: bool = False,
std3_rules: bool = False,
transitional: bool = False,
) -> bytes:
"""Encode a Unicode domain name into its ASCII (A-label) form.
Splits the input on label separators (only ``U+002E`` if ``strict`` is
set; otherwise also IDEOGRAPHIC FULL STOP ``U+3002``, FULLWIDTH FULL
STOP ``U+FF0E``, and HALFWIDTH IDEOGRAPHIC FULL STOP ``U+FF61``),
encodes each label with :func:`alabel`, and rejoins them with ``.``.
Optionally pre-processes the input through :func:`uts46_remap`.View on GitHub (pinned to d7d0d0a394)
Solutions
- Remove or replace the reported codepoint (U+XXXX) with an LDH-safe character before encoding.
- Call uts46_remap(domain, std3_rules=False) to relax the STD3 rules so more ASCII punctuation is tolerated, if your use case allows it.
- Sanitise the domain at the input boundary: NFC-normalise, strip control/format categories, and restrict to an allow-list before handing to idna.
Example fix
// before
idna.encode('a_b.example', uts46=True, std3_rules=True) # '_' disallowed
// after
idna.encode('a_b.example', uts46=True, std3_rules=False) # tolerate '_'
# or strip non-LDH first:
import unicodedata
clean = ''.join(c for c in domain if unicodedata.category(c)[0] not in ('C','Z'))
idna.encode(clean, uts46=True) Defensive patterns
Strategy: validation
Validate before calling
import idna
def uts46_safe(domain: str, std3_rules: bool = False) -> str:
try:
return idna.uts46_remap(domain, std3_rules=std3_rules)
except idna.InvalidCodepoint:
raise ValueError(f'domain {domain!r} contains a UTS #46-disallowed codepoint')
remapped = uts46_safe(domain, std3_rules=False) Type guard
import unicodedata
def is_uts46_plausible(s) -> bool:
if not isinstance(s, str):
return False
return all(unicodedata.category(c)[0] not in ('C', 'Z', 'M') for c in s) Try / catch
import idna
try:
encoded = idna.encode(domain, uts46=True)
except idna.InvalidCodepoint as err:
raise ValueError(f'invalid domain {domain!r}: {err}') from err Prevention
- NFC-normalise and strip control/format/whitespace categories before encoding.
- Use std3_rules=False in uts46_remap/encode when you need to tolerate ASCII punctuation like '_'.
- Keep vendored idna/Python current so the UTS #46 tables match modern input.
When it happens
Trigger: Calling uts46_remap() directly, or encode()/decode() with uts46=True, on a domain containing a codepoint UTS #46 marks disallowed — control characters, most symbols/punctuation, emoji, or private-use codepoints. With std3_rules=True (default for uts46_remap) additional ASCII punctuation like '_' also falls here when no mapping is defined.
Common situations: Emoji or symbol hostnames, underscores in uts46=True mode, copy-pasted domains with invisible control/format characters, private-use or unassigned codepoints, or input from systems that did no Unicode normalisation.
Related errors
- Codepoint {_unot(cp_value)} not allowed at position {pos + 1
- Codepoint {_unot(cp_value)} at position {pos + 1} of {label!
- 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
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/7bfe53c604a77602.json.
Report an issue: GitHub.