{"record":{"id":"38ddd2455e6eae50","repo":"nodejs/node","slug":"name-is-invalid-name-r","errorCode":null,"errorMessage":"name is invalid: {name!r}","messagePattern":"name is invalid: (.+?)","errorType":"validation","errorClass":"InvalidName","httpStatus":null,"severity":"error","filePath":"tools/gyp/pylib/packaging/utils.py","lineNumber":45,"sourceCode":"class InvalidSdistFilename(ValueError):\n    \"\"\"\n    An invalid sdist filename was found, users should refer to the packaging user guide.\n    \"\"\"\n\n\n# Core metadata spec for `Name`\n_validate_regex = re.compile(\n    r\"^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$\", re.IGNORECASE\n)\n_canonicalize_regex = re.compile(r\"[-_.]+\")\n_normalized_regex = re.compile(r\"^([a-z0-9]|[a-z0-9]([a-z0-9-](?!--))*[a-z0-9])$\")\n# PEP 427: The build number must start with a digit.\n_build_tag_regex = re.compile(r\"(\\d+)(.*)\")\n\n\ndef canonicalize_name(name: str, *, validate: bool = False) -> NormalizedName:\n    if validate and not _validate_regex.match(name):\n        raise InvalidName(f\"name is invalid: {name!r}\")\n    # This is taken from PEP 503.\n    value = _canonicalize_regex.sub(\"-\", name).lower()\n    return cast(NormalizedName, value)\n\n\ndef is_normalized_name(name: str) -> bool:\n    return _normalized_regex.match(name) is not None\n\n\ndef canonicalize_version(\n    version: Union[Version, str], *, strip_trailing_zero: bool = True\n) -> str:\n    \"\"\"\n    This is very similar to Version.__str__, but has one subtle difference\n    with the way it handles the release segment.\n    \"\"\"\n    if isinstance(version, str):\n        try:","sourceCodeStart":27,"sourceCodeEnd":63,"githubUrl":"https://github.com/nodejs/node/blob/1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e/tools/gyp/pylib/packaging/utils.py#L27-L63","documentation":"packaging.utils.InvalidName raised by canonicalize_name(name, validate=True) when name fails the core-metadata Name regex ^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$ (case-insensitive). Validation is opt-in; the default validate=False path canonicalizes anything without checking.","triggerScenarios":"Calling canonicalize_name(name, validate=True) with a name that is empty, starts/ends with a non-alphanumeric character, contains characters outside [A-Z0-9._-], or is a single underscore/dot/dash.","commonSituations":"Validating user-typed project names from a form or CLI, or sanitizing package names read from untrusted metadata before publishing.","solutions":["Strip whitespace and confirm the name matches the regex before calling canonicalize_name(validate=True).","Catch InvalidName and surface a user-friendly validation message at the input boundary.","Use is_normalized_name() for a softer check, or canonicalize_name(validate=False) when you only want normalization."],"exampleFix":"# before\nname = canonicalize_name(raw, validate=True)\n\n# after\ntry:\n    name = canonicalize_name(raw, validate=True)\nexcept InvalidName:\n    raise ValueError(f'Please enter a valid project name: {raw!r}')","handlingStrategy":"validation","validationCode":"import re\n_name_re = re.compile(r'^([A-Z0-9]|[A-Z0-9][A-Z0-9._-]*[A-Z0-9])$', re.IGNORECASE)\ndef is_valid_name(name: str) -> bool:\n    return bool(_name_re.match(name))","typeGuard":"from packaging.utils import canonicalize_name, InvalidName\ndef valid_name_or_none(name: str):\n    try:\n        return canonicalize_name(name, validate=True)\n    except InvalidName:\n        return None","tryCatchPattern":"try:\n    norm = canonicalize_name(raw, validate=True)\nexcept InvalidName as e:\n    raise ValueError(f'Invalid project name: {e}') from e","preventionTips":["Validate names at form/CLI input boundaries.","Restrict accepted characters to letters, digits, ., _, -.","Use validate=False when only normalization is needed."],"tags":["packaging","utils","validation","naming","core-metadata"],"backgroundTag":null,"analyzedSha":"1b2de5e052fc0fb95fd7fb6846dcec4ade598e9e","analyzedAt":"2026-08-13T00:53:24.642Z","schemaVersion":2},"datasetVersion":"2026-08-13T04:17:16.726Z"}