Skip to content

🐛 YAML-related values in config dict are never used #660

@HomerusJa

Description

@HomerusJa

The SettingsConfigDict provides three keys related to the YamlConfigSettingsSource:

class SettingsConfigDict(ConfigDict, total=False):
    # ...
    yaml_file: PathType | None
    yaml_file_encoding: str | None
    yaml_config_section: str | None
    # ...

These are lines 65-67 from pydantic_settings.main in v2.10.1. Based on the presence of these values, you would expect them to be used in the configuration of the sources (lines 349-432 in main.py for me), but they aren't. In fact, they are never used in the entire file.

The initialised settings sources are:

  • DefaultSettingsSource
  • InitSettingsSource
  • EnvSettingsSource
  • DotEnvSettingsSource
  • SecretsSettingsSource

🔁 Reproduction

You can replicate this behaviour using the following two files in the same directory. Run main.py with the command uv run --script main.py.

# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "pydantic-settings[yaml]",
# ]
# ///


# main.py
from pydantic_settings import (
    BaseSettings,
    SettingsConfigDict,
)


class Config(BaseSettings):
    model_config = SettingsConfigDict(yaml_file="config.yaml")

    some_value: int
    another_one: str


settings = Config()
# config.yaml
some_value: 2
another_one: "A random string"

When running this, the default behaviour in this case (where no setting sources are specified except the uninitialized YamlConfigSettingsSource) is that an empty dictionary of values is returned from BaseSettings._settings_build_values, as seen in lines 430 to 432.

            # no one should mean to do this, but I think returning an empty dict is marginally preferable
            # to an informative error and much better than a confusing error
            return {}

This leads to the following error in our case:

Traceback (most recent call last):
  File "/home/jakob/code/pydantic_settings_bug/main.py", line 23, in <module>
    settings = Config()
  File "/home/jakob/.cache/uv/environments-v2/main-e030d240e66b6dd2/lib/python3.13/site-packages/pydantic_settings/main.py", line 188, in __init__
    super().__init__(
    ~~~~~~~~~~~~~~~~^
        **__pydantic_self__._settings_build_values(
        ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
    ...<27 lines>...
        )
        ^
    )
    ^
  File "/home/jakob/.cache/uv/environments-v2/main-e030d240e66b6dd2/lib/python3.13/site-packages/pydantic/main.py", line 253, in __init__
    validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
pydantic_core._pydantic_core.ValidationError: 2 validation errors for Config
some_value
  Field required [type=missing, input_value={}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.11/v/missing
another_one
  Field required [type=missing, input_value={}, input_type=dict]
    For further information visit https://errors.pydantic.dev/2.11/v/missing

As you can see, input_value={} shows the default behavior mentioned above.

✅ Workaround

My current solution is to use the settings_customise_sources hook. Following is a working example:

# /// script
# requires-python = ">=3.13"
# dependencies = [
#     "pydantic-settings[yaml]",
# ]
# ///


# main.py
from pydantic_settings import (
    BaseSettings,
    PydanticBaseSettingsSource,
    YamlConfigSettingsSource,
)


class Config(BaseSettings):
    some_value: int
    another_one: str

    @classmethod
    def settings_customise_sources(
        cls,
        settings_cls: type[BaseSettings],
        init_settings: PydanticBaseSettingsSource,
        env_settings: PydanticBaseSettingsSource,
        dotenv_settings: PydanticBaseSettingsSource,
        file_secret_settings: PydanticBaseSettingsSource,
    ) -> tuple[PydanticBaseSettingsSource, ...]:
        return (
            YamlConfigSettingsSource(
                settings_cls=settings_cls,
                yaml_file="config.yaml",
            ),
        )


settings = Config()
print(settings)

💡 Proposed Fixes

A couple of options for resolving this:

  • Remove the YAML-related keys (yaml_file, etc.) from SettingsConfigDict. These are currently misleading and unused.
  • Support them properly by integrating YamlConfigSettingsSource into the default source chain, respecting these values.
    • This would introduce a breaking change to the settings_customise_sources hook, since the number of parameters would need to grow.
    • An opt-in mechanism (e.g., via use_yaml=True) could soften the impact.

Removing the unused config keys would also technically be a breaking change, but given they currently do nothing, it seems the cleaner path.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions