p-e-w/heretic · error · TypeError
{cls.__name__} must not define __init__(). Use an optional i
Error message
{cls.__name__} must not define __init__(). Use an optional init(ctx) method for plugin-specific initialization. What it means
Plugin subclasses are forbidden from defining their own __init__. The Plugin metaclass/base class handles construction itself, and plugin-specific initialization must go through an optional init(ctx) hook instead. This keeps plugin lifecycle uniform across the framework.
Source
Thrown at src/heretic/plugin.py:242
if not isinstance(settings, settings_model):
raise TypeError(
f"{self.__class__.__name__}.settings must be an instance of "
f"{settings_model.__name__}"
)
self.settings = settings
self.heretic_settings = heretic_settings
@classmethod
def validate_contract(cls) -> None:
"""
Validate the plugin contract.
- Plugins must not define a constructor (`__init__`). Initialization is
handled by `Plugin.__init__` and an optional `init(ctx)` method.
- Plugin subclasses may define `settings: <BaseModelSubclass>` to declare a settings schema.
"""
if "__init__" in cls.__dict__:
raise TypeError(
f"{cls.__name__} must not define __init__(). "
"Use an optional init(ctx) method for plugin-specific initialization."
)
@classmethod
def get_settings_model(cls) -> type[BaseModel] | None:
"""
Return the plugin settings model, if present.
- If the plugin has a `settings: <BaseModelSubclass>` type annotation,
that type is used as the settings schema.
- Otherwise: no settings schema.
"""
def unwrap_settings_type(tp: Any) -> Any:
"""Unwrap `Annotated[T, ...]`."""
while True:
origin = get_origin(tp)
if origin is Annotated:View on GitHub (pinned to bedb94ef11)
Solutions
- Remove the __init__ method from the plugin class.
- Move initialization logic into an optional `init(self, ctx)` method.
- Pass any needed configuration via the plugin's `settings` (a pydantic BaseModel) rather than constructor arguments.
Example fix
// before
class MyScorer(Plugin):
def __init__(self, model):
self.model = model
// after
class MyScorer(Plugin):
settings: MySettings
def init(self, ctx):
self.model = load_model(self.settings.model_name) Defensive patterns
Strategy: validation
Validate before calling
import inspect assert "__init__" not in MyPlugin.__dict__, "Plugin must not define __init__; use init(ctx)"
Try / catch
try:
Plugin.validate_contract(MyPlugin)
except TypeError as e:
if "must not define __init__" in str(e):
print(f"Fix plugin {MyPlugin.__name__}: move init logic to init(ctx)")
else:
raise Prevention
- Never write __init__ in Plugin subclasses; use init(ctx).
- Ship a unit test that calls validate_contract on every plugin class.
- Configure settings via a pydantic settings model instead of constructor args.
When it happens
Trigger: Defining a plugin class (subclass of the heretic Plugin base) with an `__init__` method in its class body; validate_contract is invoked via _load_and_init_scorers when plugins are loaded.
Common situations: Porting a scorer class written as a plain class into a heretic plugin while keeping its old constructor; copying a pydantic model-style class that relies on custom __init__ logic.
Related errors
- Plugin '{name}' does not export a class named '{class_name}'
- You must append the plugin class name to the filepath like t
- File-based plugin must use the form 'path/to/plugin.py:Class
- Could not load plugin '{name}' (invalid module spec)
- Import-based plugin must use the form 'fully.qualified.modul
AI-assisted analysis of p-e-w/heretic@bedb94ef11 (2026-08-29).
Data as JSON: /api/errors/89e4c6336b2514e1.
Report an issue: GitHub.