{"id":"1b8457738a4a9c1d","repo":"pypa/pip","slug":"label-must-be-in-normalization-form-c","errorCode":null,"errorMessage":"Label must be in Normalization Form C","messagePattern":"Label must be in Normalization Form C","errorType":"validation","errorClass":"IDNAError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/idna/core.py","lineNumber":216,"sourceCode":"    :raises IDNAError: If any of the hyphen restrictions are violated.\n    \"\"\"\n    if label[2:4] == \"--\":\n        raise IDNAError(\"Label has disallowed hyphens in 3rd and 4th position\")\n    if label[0] == \"-\" or label[-1] == \"-\":\n        raise IDNAError(\"Label must not start or end with a hyphen\")\n    return True\n\n\ndef check_nfc(label: str) -> None:\n    \"\"\"Require that a label is in Unicode Normalization Form C.\n\n    :param label: The label to check.\n    :raises IDNAError: If ``label`` differs from its NFC normalisation.\n    \"\"\"\n    if len(label) > _max_input_length:\n        raise IDNAError(\"Label too long\")\n    if unicodedata.normalize(\"NFC\", label) != label:\n        raise IDNAError(\"Label must be in Normalization Form C\")\n\n\ndef valid_contextj(label: str, pos: int) -> bool:\n    \"\"\"Validate the CONTEXTJ rules from :rfc:`5892` Appendix A.\n\n    These rules govern the contextual use of the joiner codepoints\n    ``U+200C`` (ZERO WIDTH NON-JOINER, Appendix A.1) and ``U+200D``\n    (ZERO WIDTH JOINER, Appendix A.2) within a label.\n\n    :param label: The label containing the codepoint.\n    :param pos: Index of the joiner codepoint within ``label``.\n    :returns: ``True`` if the codepoint at ``pos`` satisfies its CONTEXTJ\n        rule, ``False`` otherwise (including when the codepoint at\n        ``pos`` is not a recognised joiner).\n    :raises ValueError: If an adjacent codepoint has no Unicode name when\n        determining its combining class.\n    :raises IDNAError: If ``label`` exceeds the defensive input length limit.\n    \"\"\"","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/idna/core.py#L198-L234","documentation":"IDNAError from check_nfc: a label that is not in Unicode Normalization Form C is rejected. RFC 5891 requires U-labels to be in NFC so that visually identical sequences have a single canonical form, preventing homograph ambiguity. The check compares unicodedata.normalize('NFC', label) to the original.","triggerScenarios":"A label containing decomposed characters (e.g. 'á' represented as 'a' + '́' instead of the precomposed U+00E1), or any sequence whose NFC differs from the input. Triggered during alabel/check_label/check_nfc.","commonSituations":"Data entered on systems that decompose accents (macOS HFS+ filenames, some NFD-by-default pipelines); copy-paste from sources using compatibility forms; concatenation of fragments normalized differently.","solutions":["Normalize the label to NFC before encoding: unicodedata.normalize('NFC', label).","Configure upstream storage/transport to preserve NFC (or normalize on ingest).","Run a self-check: assert unicodedata.normalize('NFC', label) == label before idna."],"exampleFix":"# before\nidna.encode('á')  # 'a' + combining acute -> not NFC -> Label must be in NFC\n\n# after\nimport unicodedata\nidna.encode(unicodedata.normalize('NFC', 'á'))  # becomes 'á'","handlingStrategy":"validation","validationCode":"import unicodedata\ndef ensure_nfc(label: str) -> str:\n    n = unicodedata.normalize('NFC', label)\n    if n != label:\n        raise ValueError('label is not in NFC')\n    return n","typeGuard":"import unicodedata\ndef is_nfc(label: str) -> bool:\n    return unicodedata.normalize('NFC', label) == label","tryCatchPattern":"from idna import IDNAError\ntry:\n    idna.encode(label)\nexcept IDNAError as e:\n    if 'Normalization Form C' in str(e):\n        import unicodedata\n        label = unicodedata.normalize('NFC', label)  # retry normalized\n    else:\n        raise","preventionTips":["Normalize all input to NFC at the system ingress.","Run a self-check: assert unicodedata.normalize('NFC', s) == s before idna.","Be wary of NFD-by-default filesystems (legacy macOS HFS+) when reading labels."],"tags":["idna","unicode","normalization","nfc","rfc5891","pip"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}