{"record":{"id":"7a32dac534fdf252","repo":"p-e-w/heretic","slug":"self-class-name-requires-settings-to-be","errorCode":null,"errorMessage":"{self.__class__.__name__} requires settings to be validated","messagePattern":"(.+?) requires settings to be validated","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/heretic/plugin.py","lineNumber":221,"sourceCode":"        state outside the pinned config, for example:\n        - It calls an external service (e.g. an LLM judge over the OpenAI API).\n        - It reads credentials or config from the environment (env vars, files).\n        - It is otherwise non-deterministic (network, wall-clock, unseeded RNG).\n\n        Defaults to False; override to True in your plugin class if any of the\n        above DO NOT apply.\n        \"\"\"\n        return False\n\n    def __init__(\n        self, *, heretic_settings: HereticSettings, settings: BaseModel | None = None\n    ):\n        # Plugins that declare a settings schema should always receive\n        # validated plugin settings from the evaluator.\n        settings_model = self.__class__.get_settings_model()\n        if settings_model is not None:\n            if settings is None:\n                raise ValueError(\n                    f\"{self.__class__.__name__} requires settings to be validated\"\n                )\n            if not isinstance(settings, settings_model):\n                raise TypeError(\n                    f\"{self.__class__.__name__}.settings must be an instance of \"\n                    f\"{settings_model.__name__}\"\n                )\n        self.settings = settings\n        self.heretic_settings = heretic_settings\n\n    @classmethod\n    def validate_contract(cls) -> None:\n        \"\"\"\n        Validate the plugin contract.\n\n        - Plugins must not define a constructor (`__init__`). Initialization is\n          handled by `Plugin.__init__` and an optional `init(ctx)` method.\n        - Plugin subclasses may define `settings: <BaseModelSubclass>` to declare a settings schema.","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/p-e-w/heretic/blob/bedb94ef117a271532ac2058447fbc165d5051bd/src/heretic/plugin.py#L203-L239","documentation":"The plugin class declares a settings schema (get_settings_model() is not None), but Plugin.__init__ was called with settings=None. The library requires that schema-declaring plugins always receive validated settings, so it raises ValueError rather than running with unvalidated/absent config.","triggerScenarios":"Instantiating a plugin subclass directly as MyPlugin(heretic_settings=s) without passing settings; a custom evaluator/driver that bypasses the normal settings-validation path; the settings table for the plugin namespace is missing from the TOML config so None was forwarded.","commonSituations":"Hand-written test harness constructing the plugin manually; plugin recently gained a settings annotation while old callers still construct it without settings; config file lacking the [plugin_namespace] table.","solutions":["Pass a validated settings instance of the plugin's settings model to the constructor.","Add the plugin's namespace table to your TOML config so the evaluator builds and validates settings automatically.","If the plugin truly needs no settings, remove the `settings: <Model>` annotation from the class so get_settings_model() returns None."],"exampleFix":"# before\nplugin = MyScorer(heretic_settings=hs)\n# after\nplugin = MyScorer(heretic_settings=hs, settings=MyScorerSettings())","handlingStrategy":"validation","validationCode":"model = MyScorer.get_settings_model()\nassert model is not None, \"plugin declares settings; supply validated settings\"\nsettings = model(**cfg)\nplugin = MyScorer(heretic_settings=hs, settings=settings)","typeGuard":null,"tryCatchPattern":"try:\n    plugin = MyScorer(heretic_settings=hs, settings=settings)\nexcept ValueError as e:\n    sys.exit(f\"{e} — add the plugin's [settings] table to your config\")","preventionTips":["Never construct schema-declaring plugins without settings.","Keep the plugin's TOML namespace table present in your config.","If settings are optional, remove the settings annotation instead of passing None."],"tags":["plugin","settings","validation","pydantic"],"backgroundTag":"missing-settings","analyzedSha":"bedb94ef117a271532ac2058447fbc165d5051bd","analyzedAt":"2026-08-29T08:38:06.692Z","schemaVersion":2},"datasetVersion":"2026-08-29T12:17:43.993Z"}