p-e-w/heretic · error · TypeError

{self.__class__.__name__}.settings must be an instance of {s

Error message

{self.__class__.__name__}.settings must be an instance of {settings_model.__name__}

What it means

The plugin declares a settings model, settings were provided, but they are not an instance of that exact pydantic model class. Plugin.__init__ raises TypeError to guarantee plugins only ever see settings validated against their declared schema.

Source

Thrown at src/heretic/plugin.py:225

        Defaults to False; override to True in your plugin class if any of the
        above DO NOT apply.
        """
        return False

    def __init__(
        self, *, heretic_settings: HereticSettings, settings: BaseModel | None = None
    ):
        # Plugins that declare a settings schema should always receive
        # validated plugin settings from the evaluator.
        settings_model = self.__class__.get_settings_model()
        if settings_model is not None:
            if settings is None:
                raise ValueError(
                    f"{self.__class__.__name__} requires settings to be validated"
                )
            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__(). "

View on GitHub (pinned to bedb94ef11)

Solutions

  1. Construct settings with the exact model named in the error: SettingsModel(**config_dict).
  2. Validate your TOML table through the evaluator's normal path so it instantiates the right model.
  3. If you deliberately changed the schema, update get_settings_model()/the annotation and all construction sites together.

Example fix

# before
plugin = MyScorer(heretic_settings=hs, settings={"threshold": 0.5})
# after
plugin = MyScorer(heretic_settings=hs, settings=MyScorerSettings(threshold=0.5))
Defensive patterns

Strategy: type-guard

Validate before calling

model = MyScorer.get_settings_model()
assert isinstance(raw_settings, model), f"expected {model.__name__}, got {type(raw_settings).__name__}"

Type guard

def has_valid_settings(plugin: object, model: type) -> bool:
    return isinstance(getattr(plugin, "settings", None), model)

Try / catch

try:
    plugin = MyScorer(heretic_settings=hs, settings=raw)
except TypeError as e:
    sys.exit(f"{e} — construct settings via {MyScorer.get_settings_model()}(**cfg)")

Prevention

When it happens

Trigger: Passing a raw dict, a different BaseModel subclass, or another plugin's settings instance into __init__; a custom evaluator wiring settings from the wrong [namespace] table; subclassing a plugin but reusing the parent's settings class while the evaluator supplies the child's.

Common situations: Refactors that renamed or replaced the settings model while old callers construct the old type; tests passing mocks/dicts; two plugins with similarly named settings models getting crossed in a config.

Understand the failure class

Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.

Related errors


AI-assisted analysis of p-e-w/heretic@bedb94ef11 (2026-08-29). Data as JSON: /api/errors/843a35c586ce46b8. Report an issue: GitHub.