This rule raises an issue when a Pydantic model class inherits from two or more base classes that each define a model_config attribute, either directly or through their own inheritance chain. The rule uses the full C3 Method Resolution Order (MRO) when inspecting each direct base class. This means it also detects configurations defined in grandparent classes that are inherited through intermediate base classes without redefining model_config.

Note that the rule checks for the structural presence of model_config in multiple bases and does not compare the actual ConfigDict values. Even if two bases currently define identical configurations, the rule still raises an issue. This is intentional: identical configurations can diverge independently in future code changes, and Pydantic’s non-MRO merge behavior remains unpredictable regardless of whether the values differ at analysis time.

Why is this an issue?

Pydantic models use the model_config class attribute with a ConfigDict to control validation, serialization, and other behaviors.

When a Pydantic model inherits from a single base class, configuration inheritance works as expected: the child model inherits the parent’s configuration, and any model_config defined on the child overrides the parent’s settings.

However, when a model inherits from multiple base classes that each define their own model_config, Pydantic does not follow Python’s standard C3 Method Resolution Order (MRO) for merging configurations. Instead, it uses its own internal merging strategy, which means you cannot reliably predict which configuration settings will be applied to your model.

For example, if Base1 enables a setting that converts string values to lowercase and Base2 enables a setting that converts string values to uppercase, and your model inherits from both, the resulting behavior is undefined. The final configuration may use either setting, neither, or potentially even raise an error, depending on Pydantic’s internal implementation details.

This unpredictability makes the code harder to understand and maintain. Developers reading the code cannot easily determine what configuration will be applied without deep knowledge of Pydantic’s internals, and the behavior may change between Pydantic versions.

Exceptions

The rule does not raise an issue when the model class itself explicitly defines its own model_config. In that case, the developer has made the configuration explicit, and Pydantic will use that definition directly, making the resolution predictable.

The Pydantic documentation explicitly warns: "If your model inherits from multiple bases, Pydantic currently doesn’t follow the [MRO] for configuration merging."

What is the potential impact?

The unpredictable configuration merging can lead to several problems:

How to fix it

Use single inheritance and explicitly define the desired configuration on the final model class. This makes the configuration explicit and predictable.

Code examples

Noncompliant code example

from pydantic import BaseModel, ConfigDict

class Base1(BaseModel):
    model_config = ConfigDict(str_to_lower=True)

class Base2(BaseModel):
    model_config = ConfigDict(str_to_upper=True)

class Model(Base1, Base2):  # Noncompliant
    x: str

Compliant solution

from pydantic import BaseModel, ConfigDict

class Base1(BaseModel):
    model_config = ConfigDict(str_to_lower=True)

class Model(Base1):
    model_config = ConfigDict(str_to_lower=True)
    x: str

The rule also flags cases where a model_config is inherited indirectly through an intermediate class. In the example below, Intermediate does not define its own model_config, but it inherits one from Base1. Because Model inherits from both Intermediate (which carries Base1’s config) and `Base2 (which has its own config), the MRO-based check raises an issue. Note that even though both configs are currently identical (frozen=True), the rule still raises an issue because the configs can diverge independently in future changes.

Noncompliant code example

from pydantic import BaseModel, ConfigDict

class Base1(BaseModel):
    model_config = ConfigDict(frozen=True)

class Base2(BaseModel):
    model_config = ConfigDict(frozen=True)

class Intermediate(Base1):
    pass

class Model(Intermediate, Base2):  # Noncompliant
    x: str

Compliant solution

from pydantic import BaseModel, ConfigDict

class Base1(BaseModel):
    model_config = ConfigDict(frozen=True)

class Intermediate(Base1):
    pass

class Model(Intermediate):
    model_config = ConfigDict(frozen=True)
    x: str

Resources

Documentation