xai-org/x-algorithm · error · ValueError

Duplicate generator registration for {key!r}: {existing.__na

Error message

Duplicate generator registration for {key!r}: {existing.__name__} vs {cls.__name__}

What it means

The generator registry is keyed by TASK_GENERATOR_TYPE and refuses two different classes claiming the same key, because build() could not disambiguate which one to instantiate. Re-registering the exact same class is idempotent, but a second distinct class with the same type raises ValueError.

Source

Thrown at grox/core/generators/registry.py:19

from grox.config.config import TaskGeneratorConfig
from grox.core.generators.task_generator import TaskGenerator

_REGISTRY: dict[str, type[TaskGenerator]] = {}


def register_task_generator(cls: type[TaskGenerator]) -> type[TaskGenerator]:
    if not (isinstance(cls, type) and issubclass(cls, TaskGenerator)):
        raise TypeError(
            f"register_task_generator expects a TaskGenerator subclass, got {cls!r}"
        )
    if not cls.TASK_GENERATOR_TYPE:
        raise ValueError(
            f"{cls.__name__} must set TASK_GENERATOR_TYPE to be registered"
        )
    key = cls.TASK_GENERATOR_TYPE
    existing = _REGISTRY.get(key)
    if existing is not None and existing is not cls:
        raise ValueError(
            f"Duplicate generator registration for {key!r}: {existing.__name__} vs {cls.__name__}"
        )
    _REGISTRY[key] = cls
    return cls


def build(cfg: TaskGeneratorConfig) -> TaskGenerator:
    cls = _REGISTRY.get(cfg.type)
    if cls is None:
        raise ValueError(
            f"No task generator registered for type {cfg.type!r}. Registered: {sorted(_REGISTRY)}"
        )
    return cls.from_config(cfg)


def registered_types() -> set[str]:
    return set(_REGISTRY)

View on GitHub (pinned to 24c60942c5)

Solutions

  1. Give the new generator a unique TASK_GENERATOR_TYPE value.
  2. If the class was copy-pasted, rename its type constant.
  3. If it's a re-register of the same logical class imported twice, normalize imports (single canonical module path) or guard with a registry check.
  4. Deregister/reset the registry in test setups between suites.

Example fix

# before
class GenA(TaskGenerator):
    TASK_GENERATOR_TYPE = "tasks"
class GenB(TaskGenerator):
    TASK_GENERATOR_TYPE = "tasks"  # duplicate

# after
class GenB(TaskGenerator):
    TASK_GENERATOR_TYPE = "tasks_b"
Defensive patterns

Strategy: validation

Validate before calling

from grox.core.generators.registry import _REGISTRY

if cls.TASK_GENERATOR_TYPE in _REGISTRY:
    assert _REGISTRY[cls.TASK_GENERATOR_TYPE] is cls, "duplicate type key"

Type guard

def registration_is_unique(cls) -> bool:
    from grox.core.generators.registry import _REGISTRY
    existing = _REGISTRY.get(cls.TASK_GENERATOR_TYPE)
    return existing is None or existing is cls

Try / catch

try:
    register_task_generator(cls)
except ValueError as e:
    if "Duplicate generator registration" in str(e):
        cls.TASK_GENERATOR_TYPE = f"{cls.TASK_GENERATOR_TYPE}_{cls.__module__}"
        register_task_generator(cls)
    else:
        raise

Prevention

When it happens

Trigger: Two TaskGenerator subclasses in the same process both set TASK_GENERATOR_TYPE to the same string; double imports of a module under different names (e.g. path + package import) creating distinct class objects; copy-pasted generator subclass that kept the parent's type constant.

Common situations: See trigger scenarios.

Related errors


AI-assisted analysis of xai-org/x-algorithm@24c60942c5 (2026-08-28). Data as JSON: /api/errors/64340ba553990f30. Report an issue: GitHub.