Skip to content

CliApp.run() ignores env_file from model_config #846

@xbeastx

Description

@xbeastx

Description

CliApp.run() does not load the env_file specified in model_config. Direct instantiation of the same BaseSettings subclass correctly loads the .env file.

Root Cause

_settings_init_sources defaults _env_file to None, while BaseSettings.__init__ defaults it to ENV_FILE_SENTINEL.

The guard on line 311 of main.py:

env_file = _env_file if _env_file != ENV_FILE_SENTINEL else cls.model_config.get('env_file')

only falls through to model_config when _env_file == ENV_FILE_SENTINEL. Since _settings_init_sources defaults to None, and None != ENV_FILE_SENTINEL (Path('')) is True, the model_config value is never used.

CliApp.run() calls _settings_init_sources(**model_init_data) without including _env_file, so the method receives None and skips the .env file entirely.

Call chain

  1. CliApp.run(MySettings, cli_args) builds model_init_data with only _cli_parse_args, _cli_exit_on_error, _cli_settings_source — no _env_file.
  2. model_cls._settings_init_sources(**model_init_data) is called; _env_file falls back to its default None.
  3. None != ENV_FILE_SENTINELTrueenv_file = None.
  4. DotEnvSettingsSource(env_file=None) → no dotenv file loaded.

Minimal Reproducible Example

# .env file contains: APP_NAME=from_dotenv

from pydantic_settings import BaseSettings, CliApp, SettingsConfigDict

class MySettings(BaseSettings):
    model_config = SettingsConfigDict(env_prefix="APP_", env_file=".env")
    name: str = "default"

# Works: loads .env
s1 = MySettings()
assert s1.name == "from_dotenv"

# Bug: ignores .env, name stays "default"
s2 = CliApp.run(MySettings, [])
assert s2.name == "from_dotenv"  # FAILS

Suggested Fix

Change the default of _env_file in _settings_init_sources from None to ENV_FILE_SENTINEL:

  @classmethod
  def _settings_init_sources(
      cls,
      ...
-     _env_file: DotenvType | None = None,
+     _env_file: DotenvType | None = ENV_FILE_SENTINEL,
      ...
  )

This aligns the behavior with BaseSettings.__init__ where the default is already ENV_FILE_SENTINEL.

Versions

  • pydantic-settings: 2.13.1
  • pydantic: 2.13.0
  • Python: 3.13.9

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions