pypa/pip · error · IDNAError
Label begins with an illegal combining character
Error message
Label begins with an illegal combining character
What it means
IDNAError from check_initial_combiner: per RFC 5891 §4.2.3.2 a label must not begin with a character of Unicode general category M (Mark – Mn, Mc, Me). A leading combining mark has no base character to combine with and is rejected.
Source
Thrown at src/pip/_vendor/idna/core.py:185
if not valid_ending:
raise IDNABidiError("Label ends with illegal codepoint directionality")
return True
def check_initial_combiner(label: str) -> bool:
"""Reject labels that begin with a combining mark.
Per :rfc:`5891` §4.2.3.2 a label must not start with a character of
Unicode general category ``M`` (Mark).
:param label: The label to check.
:returns: ``True`` if the first character is not a combining mark.
:raises IDNAError: If the label begins with a combining character.
"""
if unicodedata.category(label[0])[0] == "M":
raise IDNAError("Label begins with an illegal combining character")
return True
def check_hyphen_ok(label: str) -> bool:
"""Validate the hyphen restrictions for a label.
Per :rfc:`5891` §4.2.3.1 a label must not start or end with a hyphen
(``U+002D``), and must not have hyphens in both the third and fourth
positions (the prefix reserved for A-labels).
:param label: The label to check.
:returns: ``True`` if the hyphen restrictions are satisfied.
:raises IDNAError: If any of the hyphen restrictions are violated.
"""
if label[2:4] == "--":
raise IDNAError("Label has disallowed hyphens in 3rd and 4th position")
if label[0] == "-" or label[-1] == "-":
raise IDNAError("Label must not start or end with a hyphen")View on GitHub (pinned to d7d0d0a394)
Solutions
- Ensure the first character of each label is a base character (letter/digit), not a combining mark.
- Apply unicodedata.normalize('NFC', label) first so combining marks attach to a base.
- Strip leading category-M characters: while unicodedata.category(label[0])[0]=='M': label=label[1:].
Example fix
# before
idna.encode('́test') # Label begins with illegal combining character
# after
idna.encode('test')
# or pre-normalize:
idna.encode(unicodedata.normalize('NFC', '́test')) # if a base precedes it Defensive patterns
Strategy: validation
Validate before calling
import unicodedata
def strip_leading_combiners(label: str) -> str:
while label and unicodedata.category(label[0])[0] == 'M':
label = label[1:]
return label Type guard
import unicodedata
def starts_with_base_char(label: str) -> bool:
return bool(label) and unicodedata.category(label[0])[0] != 'M' Try / catch
from idna import IDNAError
try:
idna.encode(label)
except IDNAError as e:
if 'combining character' in str(e):
label = strip_leading_combiners(label) # or normalize NFC upstream
else:
raise Prevention
- Always NFC-normalize user input before IDNA: unicodedata.normalize('NFC', s).
- Reject or strip leading category-M characters at the input boundary.
- Validate that the first character is a base letter/digit.
When it happens
Trigger: A label whose first character is a combining mark, e.g. '́abc' (combining acute) or 'ًالعرب' (Arabic tanvin leading). check_initial_combiner is called from check_label during alabel/encode.
Common situations: Strings that lost their leading base character through truncation or normalization; concatenation that puts a combining mark at the start; copy-paste from sources that include leading diacritics; NFC normalization that did not collapse a detached mark.
Related errors
- Label must be in Normalization Form C
- Unknown directionality in label {label!r} at position {idx}
- First codepoint in label {label!r} must be directionality L,
- Label has disallowed hyphens in 3rd and 4th position
- Label must not start or end with a hyphen
AI-assisted analysis of pypa/pip@d7d0d0a394 (2026-08-04).
Data as JSON: /data/errors/7f0f6a797e2074ab.json.
Report an issue: GitHub.