{"record":{"id":"8e31f161693c5de2","repo":"pypa/pip","slug":"limit-must-be-non-negative","errorCode":null,"errorMessage":"limit must be non-negative","messagePattern":"limit must be non-negative","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/pip/_vendor/packaging/tags.py","lineNumber":275,"sourceCode":"        interpreter, ABI, or platform field (or any member of a compressed tag\n        set) is empty; or if the tag does not have exactly three components.\n    :raises TooManyTagsError: If **limit** is not ``None`` and the compressed tag\n        set would generate more than **limit** tags.\n    :raises ValueError: If **limit** is negative.\n\n    .. versionadded:: 26.1\n       The *validate_order* parameter.\n\n    .. versionadded:: 26.3\n       Raises :class:`InvalidTag` when an interpreter component is not an\n       identifier, a tag component is empty, or a tag does not have exactly\n       three components.\n       Added the *limit* parameter. Raises :class:`TooManyTagsError` if the compressed\n       tag set would generate more than *limit* tags.\n    \"\"\"\n\n    if limit is not None and limit < 0:\n        raise ValueError(\"limit must be non-negative\")\n\n    component_parts = [component.split(\".\") for component in tag.split(\"-\")]\n    for parts in component_parts:\n        if \"\" in parts:\n            component = \".\".join(parts)\n            raise InvalidTag(f\"Tag {tag!r} has an empty component: {component!r}\")\n        if validate_order and parts != sorted(parts):\n            component = \".\".join(parts)\n            raise UnsortedTagsError(\n                f\"Tag component {component!r} is not in sorted order per PEP 425\"\n            )\n\n    tag_count = 1\n    for parts in component_parts:\n        tag_count *= len(parts)\n\n    if limit is not None and tag_count > limit:\n        raise TooManyTagsError(","sourceCodeStart":257,"sourceCodeEnd":293,"githubUrl":"https://github.com/pypa/pip/blob/f399c3718970b1b0e2478dac5296eb62679a9b86/src/pip/_vendor/packaging/tags.py#L257-L293","documentation":"Raised as ValueError by parse_tag (tags.py:274-275) when the limit keyword argument is not None and is negative. The limit caps how many tags a compressed tag set may expand to; a negative cap is meaningless, so it is rejected before any parsing begins.","triggerScenarios":"parse_tag('py3-none-any', limit=-1); passing a limit computed from a subtraction that went negative (e.g. budget - overshoot); a CLI/config value parsed as a signed int where 0/-1 was used as 'unlimited' by mistake.","commonSituations":"A wheel-tag budget calculator that underflows; config defaulting to -1 meaning 'no limit' in another tool but fed to packaging's limit which expects None for unlimited; off-by-one in a tag-count estimator.","solutions":["Use limit=None to mean 'no cap' rather than a negative sentinel.","Clamp the computed limit to max(0, value) before passing.","Validate user-supplied limit: if value is None or value >= 0 pass through, else raise your own clear error.","If a value like -1 is your 'unlimited' convention, translate it to None at the boundary."],"exampleFix":"# before\nparse_tag('py3-none-any', limit=budget - overshoot)  # may be -1 -> ValueError\n\n# after\nlimit = None if budget is None else max(0, budget - overshoot)\nparse_tag('py3-none-any', limit=limit)","handlingStrategy":"validation","validationCode":"def clamp_limit(v):\n    return None if v is None else max(0, int(v))","typeGuard":"def is_valid_limit(v) -> bool:\n    return v is None or (isinstance(v, int) and v >= 0)","tryCatchPattern":null,"preventionTips":["Use None (not -1) to mean 'no limit'.","Clamp computed limits with max(0, value).","Translate your tool's 'unlimited' sentinel to None at the boundary."],"tags":["packaging","tags","validation","configuration"],"backgroundTag":null,"analyzedSha":"f399c3718970b1b0e2478dac5296eb62679a9b86","analyzedAt":"2026-08-08T23:01:42.227Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}