pypa/pip · error · IDNAError

Label has disallowed hyphens in 3rd and 4th position

Error message

Label has disallowed hyphens in 3rd and 4th position

What it means

IDNAError from check_hyphen_ok: per RFC 5891 §4.2.3.1 a label must not contain hyphens in both the 3rd and 4th positions (label[2:4] == '--'). That pattern is reserved as the prefix of ACE (Punycode) A-labels ('xn--'), and a U-label matching it would be ambiguous.

Source

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

    """
    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")
    return True


def check_nfc(label: str) -> None:
    """Require that a label is in Unicode Normalization Form C.

    :param label: The label to check.
    :raises IDNAError: If ``label`` differs from its NFC normalisation.
    """
    if len(label) > _max_input_length:
        raise IDNAError("Label too long")
    if unicodedata.normalize("NFC", label) != label:
        raise IDNAError("Label must be in Normalization Form C")


def valid_contextj(label: str, pos: int) -> bool:

View on GitHub (pinned to d7d0d0a394)

Solutions

  1. Avoid placing '--' at positions 3-4; use a different separator or pad the prefix to 3+ characters before the hyphens.
  2. If the input is already an A-label (xn--...), pass it through ulabel/decode rather than re-encoding via alabel.
  3. Rename the label so the double hyphen appears elsewhere.

Example fix

# before
idna.encode('ab--العرب')  # disallowed hyphens in 3rd and 4th position

# after
idna.encode('abc--العرب')  # double hyphen no longer at 3-4
Defensive patterns

Strategy: validation

Validate before calling

def avoids_reserved_ace_prefix(label: str) -> bool:
    return label[2:4] != '--'

Type guard

def is_safe_label_for_ace(label: str) -> bool:
    return len(label) < 4 or label[2:4] != '--'

Try / catch

from idna import IDNAError
try:
    idna.encode(label)
except IDNAError as e:
    if '3rd and 4th' in str(e):
        # relocate the double-hyphen away from positions 3-4
        raise ValueError('reserved ACE prefix shape; rename label') from e
    raise

Prevention

When it happens

Trigger: A non-ASCII U-label whose 3rd and 4th characters are both hyphens, e.g. 'ab--العرب'. Encountered when alabel/check_label processes a label that was not already an xn-- A-label but happens to match the reserved prefix shape.

Common situations: Auto-generated names of the form '<2chars>--<rest>'; user handles containing a double-hyphen after a short prefix; data from a system that uses '--' as a separator glued directly to a 2-char prefix.

Related errors


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