When using CliApp.run() with a BaseSettings class that contains a field with a self-referential model type, a RecursionError occurs. The CLI argument parser enters infinite recursion when trying to introspect the self-referential field structure.
Environment:
pydantic==2.12.5
pydantic-settings==2.13.0
pydantic_core==2.41.5
annotated-types==0.7.0
typing_extensions==4.15.0
python-dotenv==1.2.1
typing-inspection==0.4.2
Minimal Reproducible Example (behavior is the same without Optional and default value):
from typing import Optional
from pydantic import BaseModel
from pydantic_settings import BaseSettings, CliApp
class Foo(BaseModel):
foo: Optional["Foo"] = None
Foo.model_rebuild()
class RecursiveSettings(BaseSettings):
foo: Foo
def cli_cmd(self):
pass
CliApp.run(RecursiveSettings)
Expected behavior:
Works the same as RecursiveSettings().cli_cmd() parses values from specified sources without raising recursion error or raising a more specific error if there is an internal reason for this behavior
Actual behavior:
Raises RecursionError
Top part
Traceback (most recent call last):
File "/home/filip/PassionProjects/pydantic-bug/main.py", line 19, in <module>
CliApp.run(RecursiveSettings)
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic_settings/main.py", line 733, in run
sources, init_kwargs = model_cls._settings_init_sources(**model_init_data)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic_settings/main.py", line 424, in _settings_init_sources
cli_settings = CliSettingsSource[Any](
^^^^^^^^^^^^^^^^^^^^^^^
File "/usr/lib/python3.12/typing.py", line 1157, in __call__
result = self.__origin__(*args, **kwargs)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic_settings/sources/providers/cli.py", line 415, in __init__
self._connect_root_parser(
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic_settings/sources/providers/cli.py", line 906, in _connect_root_parser
self._add_parser_args(
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic_settings/sources/providers/cli.py", line 1049, in _add_parser_args
self._add_parser_submodels(
Bottom part
self._add_parser_args(
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic_settings/sources/providers/cli.py", line 1049, in _add_parser_args
self._add_parser_submodels(
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic_settings/sources/providers/cli.py", line 1242, in _add_parser_submodels
self._add_parser_args(
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic_settings/sources/providers/cli.py", line 960, in _add_parser_args
arg = _CliArg(
^^^^^^^^
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic_settings/sources/providers/cli.py", line 117, in __init__
super().__init__(**values)
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic/main.py", line 250, in __init__
validated_self = self.__pydantic_validator__.validate_python(data, self_instance=self)
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic/_internal/_model_construction.py", line 369, in init_private_attributes
default = private_attr.get_default()
^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic/fields.py", line 1453, in get_default
return _utils.smart_deepcopy(self.default) if self.default_factory is None else self.default_factory()
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
File "/home/filip/PassionProjects/pydantic-bug/.venv/lib/python3.12/site-packages/pydantic/_internal/_utils.py", line 354, in smart_deepcopy
return deepcopy(obj) # slowest way when we actually might need a deepcopy
^^^^^^^^^^^^^
RecursionError: maximum recursion depth exceeded
When using CliApp.run() with a BaseSettings class that contains a field with a self-referential model type, a RecursionError occurs. The CLI argument parser enters infinite recursion when trying to introspect the self-referential field structure.
Environment:
pydantic==2.12.5
pydantic-settings==2.13.0
pydantic_core==2.41.5
annotated-types==0.7.0
typing_extensions==4.15.0
python-dotenv==1.2.1
typing-inspection==0.4.2
Minimal Reproducible Example (behavior is the same without Optional and default value):
Expected behavior:
Works the same as
RecursiveSettings().cli_cmd()parses values from specified sources without raising recursion error or raising a more specific error if there is an internal reason for this behaviorActual behavior:
Raises RecursionError
Top part
Bottom part