{"id":"223517c19b5222ed","repo":"pypa/pip","slug":"local-must-be-a-valid-version-string-got-value-r","errorCode":null,"errorMessage":"local must be a valid version string, got {value!r}","messagePattern":"local must be a valid version string, got (.+?)","errorType":"validation","errorClass":"InvalidVersion","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/version.py","lineNumber":323,"sourceCode":"    raise InvalidVersion(msg)\n\n\ndef _validate_dev(value: object, /) -> tuple[Literal[\"dev\"], int] | None:\n    if value is None:\n        return value\n    if isinstance(value, int) and value >= 0:\n        return (\"dev\", value)\n    msg = f\"dev must be non-negative integer, got {value}\"\n    raise InvalidVersion(msg)\n\n\ndef _validate_local(value: object, /) -> LocalType | None:\n    if value is None:\n        return value\n    if isinstance(value, str) and _LOCAL_PATTERN.fullmatch(value):\n        return _parse_local_version(value)\n    msg = f\"local must be a valid version string, got {value!r}\"\n    raise InvalidVersion(msg)\n\n\n# Backward compatibility for internals before 26.0. Do not use.\nclass _Version(NamedTuple):\n    epoch: int\n    release: tuple[int, ...]\n    dev: tuple[Literal[\"dev\"], int] | None\n    pre: tuple[Literal[\"a\", \"b\", \"rc\"], int] | None\n    post: tuple[Literal[\"post\"], int] | None\n    local: LocalType | None\n\n\nclass Version(_BaseVersion):\n    \"\"\"This class abstracts handling of a project's versions.\n\n    A :class:`Version` instance is comparison aware and can be compared and\n    sorted using the standard Python interfaces.\n","sourceCodeStart":305,"sourceCodeEnd":341,"githubUrl":"https://github.com/pypa/pip/blob/d7d0d0a39494e28ec1c407bd0680e4a4d1067791/src/pip/_vendor/packaging/version.py#L305-L341","documentation":"Raised by `_validate_local(value)` when the `local` argument to `Version.from_parts`/`__replace__` is not `None` and not a string fully matching the local-version pattern `[a-z0-9]+(?:[._-][a-z0-9]+)*` (ASCII, case-insensitive). The local segment is the `+xyz` part of PEP 440 (`1.0+ubuntu.1`); any non-string, or string with invalid punctuation/empty segments, is rejected.","triggerScenarios":"`Version.from_parts(release=(1,0), local='a.b.')` (trailing separator), `local='a..b'` (empty middle), `local='a b'` (space), `local=123` (not a str), `local=''`. The validator requires `isinstance(value, str) and _LOCAL_PATTERN.fullmatch(value)`.","commonSituations":"Local segment built with a trailing/leading separator; non-ASCII characters; a non-string passed by mistake; empty string.","solutions":["Pass a string of ASCII alphanumerics separated by `.`, `_`, or `-`, with no empty runs: `local='ubuntu.1'`.","Strip stray separators and validate against the pattern before calling.","Use `Version('1.0+ubuntu.1')` for string input.","Omit `local` (`None`) if there is no local segment."],"exampleFix":"// before\nVersion.from_parts(release=(1, 0), local='a..b')  # empty segment -> raises\n\n// after\nVersion.from_parts(release=(1, 0), local='a.b')\n# or\nVersion('1.0+a.b')","handlingStrategy":"validation","validationCode":"import re\n_LOCAL_RE = re.compile(r'[a-z0-9]+(?:[._-][a-z0-9]+)*', re.IGNORECASE | re.ASCII)\n\ndef is_valid_local(value: object) -> bool:\n    return value is None or (isinstance(value, str) and bool(_LOCAL_RE.fullmatch(value)))","typeGuard":"import re\n_LOCAL_RE = re.compile(r'[a-z0-9]+(?:[._-][a-z0-9]+)*', re.IGNORECASE | re.ASCII)\n\ndef is_local_version_string(value: object) -> bool:\n    return value is None or (isinstance(value, str) and bool(_LOCAL_RE.fullmatch(value)))","tryCatchPattern":"from pip._vendor.packaging.version import Version, InvalidVersion\n\ntry:\n    v = Version.from_parts(release=release, local=local)\nexcept InvalidVersion as e:\n    if 'local' in str(e):\n        local = local.strip('._-') if isinstance(local, str) else None\n        v = Version.from_parts(release=release, local=local)\n    raise","preventionTips":["Pass `local` as ASCII alphanumerics separated by `.`, `_`, or `-` with no empty/leading/trailing separators, or `None`.","Validate against `[a-z0-9]+(?:[._-][a-z0-9]+)*` before calling.","For string input, prefer `Version('1.0+local.1')`."],"tags":["python","packaging","version","pep440","validation","from-parts","local-version"],"analyzedSha":"d7d0d0a39494e28ec1c407bd0680e4a4d1067791","analyzedAt":"2026-08-04T20:55:04.259Z","schemaVersion":2}