{"record":{"id":"71a659a5506de444","repo":"xai-org/x-algorithm","slug":"cls-name-must-set-task-generator-type-to-be","errorCode":null,"errorMessage":"{cls.__name__} must set TASK_GENERATOR_TYPE to be registered","messagePattern":"(.+?) must set TASK_GENERATOR_TYPE to be registered","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"grox/core/generators/registry.py","lineNumber":13,"sourceCode":"from grox.config.config import TaskGeneratorConfig\nfrom grox.core.generators.task_generator import TaskGenerator\n\n_REGISTRY: dict[str, type[TaskGenerator]] = {}\n\n\ndef register_task_generator(cls: type[TaskGenerator]) -> type[TaskGenerator]:\n    if not (isinstance(cls, type) and issubclass(cls, TaskGenerator)):\n        raise TypeError(\n            f\"register_task_generator expects a TaskGenerator subclass, got {cls!r}\"\n        )\n    if not cls.TASK_GENERATOR_TYPE:\n        raise ValueError(\n            f\"{cls.__name__} must set TASK_GENERATOR_TYPE to be registered\"\n        )\n    key = cls.TASK_GENERATOR_TYPE\n    existing = _REGISTRY.get(key)\n    if existing is not None and existing is not cls:\n        raise ValueError(\n            f\"Duplicate generator registration for {key!r}: {existing.__name__} vs {cls.__name__}\"\n        )\n    _REGISTRY[key] = cls\n    return cls\n\n\ndef build(cfg: TaskGeneratorConfig) -> TaskGenerator:\n    cls = _REGISTRY.get(cfg.type)\n    if cls is None:\n        raise ValueError(\n            f\"No task generator registered for type {cfg.type!r}. Registered: {sorted(_REGISTRY)}\"\n        )","sourceCodeStart":1,"sourceCodeEnd":31,"githubUrl":"https://github.com/xai-org/x-algorithm/blob/24c60942c5c5fdad3a6addffb4c6e6d2f228f04f/grox/core/generators/registry.py#L1-L31","documentation":"Every TaskGenerator subclass must declare a non-empty TASK_GENERATOR_TYPE class attribute — it is the registry key used by build() to look up generators from config. register_task_generator refuses classes with a missing, None, or empty TASK_GENERATOR_TYPE so the registry stays keyed by meaningful names.","triggerScenarios":"Registering a TaskGenerator subclass that doesn't define TASK_GENERATOR_TYPE, or defines it as None/empty string/empty value.","commonSituations":"New generator class written by copying a template without filling in the type constant; subclass meant to be abstract accidentally registered; constant renamed in a refactor.","solutions":["Add a unique non-empty class attribute: TASK_GENERATOR_TYPE = \"my_generator\".","If the class is abstract/intermediate, don't apply the decorator to it.","Use the same string you reference in TaskGeneratorConfig.type so build() resolves it."],"exampleFix":"# before\n@register_task_generator\nclass MyGen(TaskGenerator):\n    ...\n\n# after\n@register_task_generator\nclass MyGen(TaskGenerator):\n    TASK_GENERATOR_TYPE = \"my_gen\"","handlingStrategy":"validation","validationCode":"from grox.core.generators.registry import registered_types\n\nassert MyGen.TASK_GENERATOR_TYPE, \"set TASK_GENERATOR_TYPE before registering\"","typeGuard":"def has_generator_type(cls) -> bool:\n    return bool(getattr(cls, \"TASK_GENERATOR_TYPE\", None))","tryCatchPattern":"try:\n    register_task_generator(cls)\nexcept ValueError as e:\n    if \"TASK_GENERATOR_TYPE\" in str(e):\n        cls.TASK_GENERATOR_TYPE = derived_default(cls)\n        register_task_generator(cls)\n    else:\n        raise","preventionTips":["Include TASK_GENERATOR_TYPE in your generator class template.","Add a unit test asserting every registered class has a non-empty type.","Keep config type strings and TASK_GENERATOR_TYPE in one constants module."],"tags":["python","registry","configuration","class-attribute"],"backgroundTag":"missing-registry-key","analyzedSha":"24c60942c5c5fdad3a6addffb4c6e6d2f228f04f","analyzedAt":"2026-08-28T11:40:14.686Z","schemaVersion":2},"datasetVersion":"2026-08-28T16:17:29.566Z"}