{"id":"96377fd1d5d99732","repo":"pypa/pip","slug":"ill-formed-name-version-string-s","errorCode":null,"errorMessage":"Ill-formed name/version string: '%s'","messagePattern":"Ill-formed name/version string: '(.+?)'","errorType":"validation","errorClass":"DistlibException","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/distlib/util.py","lineNumber":897,"sourceCode":"\n\n# Allow spaces in name because of legacy dists like \"Twisted Core\"\nNAME_VERSION_RE = re.compile(r'(?P<name>[\\w .-]+)\\s*'\n                             r'\\(\\s*(?P<ver>[^\\s)]+)\\)$')\n\n\ndef parse_name_and_version(p):\n    \"\"\"\n    A utility method used to get name and version from a string.\n\n    From e.g. a Provides-Dist value.\n\n    :param p: A value in a form 'foo (1.0)'\n    :return: The name and version as a tuple.\n    \"\"\"\n    m = NAME_VERSION_RE.match(p)\n    if not m:\n        raise DistlibException('Ill-formed name/version string: \\'%s\\'' % p)\n    d = m.groupdict()\n    return d['name'].strip().lower(), d['ver']\n\n\ndef get_extras(requested, available):\n    result = set()\n    requested = set(requested or [])\n    available = set(available or [])\n    if '*' in requested:\n        requested.remove('*')\n        result |= available\n    for r in requested:\n        if r == '-':\n            result.add(r)\n        elif r.startswith('-'):\n            unwanted = r[1:]\n            if unwanted not in available:\n                logger.warning('undeclared extra: %s' % unwanted)","sourceCodeStart":879,"sourceCodeEnd":915,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/distlib/util.py#L879-L915","documentation":"Raised by parse_name_and_version() when its input does not match NAME_VERSION_RE, the expected 'name (version)' form used in metadata fields like Provides-Dist / Obsoletes-Dist. A DistlibException 'Ill-formed name/version string' indicates the string is not in the canonical 'foo (1.0)' shape.","triggerScenarios":"parse_name_and_version('foo 1.0') (no parens), parse_name_and_version('foo'), parse_name_and_version('foo-1.0.tar.gz'), or any value not matching 'Name (Version)'.","commonSituations":"Reading metadata fields that are inconsistently formatted, parsing sdist filenames instead of metadata values, or legacy metadata that omits parentheses.","solutions":["Provide the string in 'Name (Version)' form: 'foo (1.0)'.","If parsing a filename, use a filename parser instead and assemble 'name (version)' explicitly.","Pre-validate with the NAME_VERSION_RE regex (or packaging.utils) before calling parse_name_and_version()."],"exampleFix":"// before\nparse_name_and_version('requests-2.31.0')\n// after\nparse_name_and_version('requests (2.31.0)')","handlingStrategy":"validation","validationCode":"from distlib.util import NAME_VERSION_RE\ndef safe_parse_nv(p):\n    if not NAME_VERSION_RE.match(p):\n        raise ValueError('not in Name (Version) form: %r' % p)\n    from distlib.util import parse_name_and_version\n    return parse_name_and_version(p)","typeGuard":null,"tryCatchPattern":"from distlib.util import parse_name_and_version, DistlibException\ntry:\n    name, version = parse_name_and_version(value)\nexcept DistlibException as e:\n    if 'Ill-formed name/version string' in str(e):\n        name, version = alternative_parse(value)\n    else:\n        raise","preventionTips":["Pass metadata values in 'Name (Version)' form, not filenames.","Pre-validate against NAME_VERSION_RE before calling parse_name_and_version().","Use packaging.utils for filename-based name/version extraction."],"tags":["metadata","parsing","packaging","validation","pep440"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}