pypa/pip · error · InvalidTag

Tag {tag!r} must have exactly three components

Error message

Tag {tag!r} must have exactly three components

What it means

Raised as InvalidTag (ValueError subclass) by parse_tag (tags.py:298-301) when splitting the tag on '-' does not produce exactly three component groups (interpreter, abi, platform). The destructuring `interpreters, abis, platforms = component_parts` raises a ValueError that is wrapped into this InvalidTag. Two or four+ hyphen-delimited groups both trigger it.

Source

Thrown at src/pip/_vendor/packaging/tags.py:301

            component = ".".join(parts)
            raise UnsortedTagsError(
                f"Tag component {component!r} is not in sorted order per PEP 425"
            )

    tag_count = 1
    for parts in component_parts:
        tag_count *= len(parts)

    if limit is not None and tag_count > limit:
        raise TooManyTagsError(
            f"Compressed tag set would generate {tag_count} tags, exceeding "
            f"limit {limit}"
        )

    try:
        interpreters, abis, platforms = component_parts
    except ValueError as exc:
        raise InvalidTag(f"Tag {tag!r} must have exactly three components") from exc
    for interpreter in interpreters:
        if not interpreter.isidentifier():
            raise InvalidTag(f"Tag {tag!r} has an invalid interpreter: {interpreter!r}")
    return frozenset(
        Tag(interpreter, abi, platform_)
        for interpreter in interpreters
        for abi in abis
        for platform_ in platforms
    )


def _get_config_var(name: str, warn: bool = False) -> int | str | None:
    value: int | str | None = sysconfig.get_config_var(name)
    if value is None and warn:
        logger.debug(
            "Config variable '%s' is unset, Python ABI tag may be incorrect", name
        )
    return value

View on GitHub (pinned to f399c37189)

Solutions

  1. Normalize platform tags: replace '-' and '.' with '_' (use _normalize_string / str.replace) so the tag splits into exactly three groups.
  2. Verify the tag has exactly two hyphens: assert tag.count('-') == 2 before parse_tag.
  3. If you have the three parts already, construct Tag(interpreter, abi, platform) directly instead of parse_tag.
  4. Catch InvalidTag and report the malformed tag to the caller.

Example fix

# before
parse_tag('py3-none-macosx-10-9-x86_64')  # 5 components -> InvalidTag

# after - normalize hyphens to underscores in the platform
plat = 'macosx-10-9-x86_64'.replace('-', '_')  # 'macosx_10_9_x86_64'
parse_tag(f'py3-none-{plat}')
Defensive patterns

Strategy: validation

Validate before calling

def has_three_components(tag: str) -> bool:
    return tag.count('-') == 2

Try / catch

from packaging.tags import InvalidTag
try:
    parse_tag(tag)
except InvalidTag:
    handle_malformed(tag)

Prevention

When it happens

Trigger: parse_tag('py3-none') (2 components); parse_tag('py3-none-any-extra') (4 components); a platform string containing a hyphen that wasn't normalized to underscore (e.g. 'macosx-10-9-x86_64' style without dots).

Common situations: Wheel filenames whose platform tag legitimately contains hyphens being split naively; a tag assembled from a platform with hyphens not converted to underscores per PEP 425; truncated/corrupt tag strings.

Related errors


AI-assisted analysis of pypa/pip@f399c37189 (2026-08-08). Data as JSON: /api/errors/202d88311955ea97. Report an issue: GitHub.