{"record":{"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":327,"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":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/version.py#L309-L345","documentation":"Raised by _validate_local() (version.py:321-327) when the local version segment passed to Version.from_parts() or Version.__replace__() does not match _LOCAL_PATTERN ([a-z0-9]+(?:[._-][a-z0-9]+)*). Passing None is allowed (means no local segment). The pattern requires alphanumeric segments separated by dots, underscores, or hyphens; leading/trailing separators, spaces, or uppercase-only patterns without alphanumerics fail.","triggerScenarios":"Calling Version.from_parts(release=(1,), local='!abc') (invalid character), Version.from_parts(release=(1,), local='.abc') (leading separator), or Version.from_parts(release=(1,), local='') (empty string is not None and fails the pattern).","commonSituations":"Passing a local version string with invalid characters, an empty string instead of None to clear the segment, or a string with a leading/trailing separator.","solutions":["Pass local as a string matching [a-z0-9]+(?:[._-][a-z0-9]+)* (e.g. 'abc', 'abc.1', 'ubuntu-1').","Pass None (or omit) to clear the local segment, not an empty string.","Strip leading/trailing separators and remove invalid characters before calling."],"exampleFix":"// before\nVersion.from_parts(release=(1, 0), local='.abc')\n// after\nVersion.from_parts(release=(1, 0), local='abc')","handlingStrategy":"type-guard","validationCode":"import re\nfrom packaging.version import Version, InvalidVersion\n\n_LOCAL_PATTERN = re.compile(r\"[a-z0-9]+(?:[._-][a-z0-9]+)*\", re.IGNORECASE | re.ASCII)\n\ndef safe_from_parts(*, release, local=None, **kwargs):\n    if local is not None and (not isinstance(local, str) or not _LOCAL_PATTERN.fullmatch(local)):\n        raise ValueError(f\"local must match [a-z0-9]+(?:[._-][a-z0-9]+)*, got {local!r}\")\n    return Version.from_parts(release=release, local=local, **kwargs)","typeGuard":"import re\n\n_LOCAL_PATTERN = re.compile(r\"[a-z0-9]+(?:[._-][a-z0-9]+)*\", re.IGNORECASE | re.ASCII)\n\ndef is_valid_local(value) -> bool:\n    return value is None or (\n        isinstance(value, str) and bool(_LOCAL_PATTERN.fullmatch(value))\n    )","tryCatchPattern":"from packaging.version import InvalidVersion\n\ntry:\n    v = Version.from_parts(release=(1,), local=local)\nexcept InvalidVersion as e:\n    if \"local must be\" in str(e):\n        logger.error(\"Invalid local version segment: %s\", local)\n    raise","preventionTips":["Pass local as a string matching [a-z0-9]+(?:[._-][a-z0-9]+)*","Pass None (not '') to clear the local segment","Strip leading/trailing separators before calling"],"tags":["version","local-version","packaging","pep440"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}