Skip to content

Commit 5fd9178

Browse files
olivermeyerclaude
andcommitted
fix(settings): handle non-string loc in validation error formatting
In Pydantic v2, `loc` entries can be integers (e.g. for list-typed fields validated via RootModel). The previous single-expression format f"{prefix}{error['loc'][0]}".upper() produced meaningless env-var names like "MY_PREFIX_0" for integer locs. Add an explicit isinstance guard so integer locs fall back to the model prefix (e.g. "MY_PREFIX") rather than appending the raw integer. Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
1 parent 490b1b3 commit 5fd9178

2 files changed

Lines changed: 31 additions & 2 deletions

File tree

src/aignostics_foundry_core/settings.py

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,7 +95,10 @@ def load_settings(settings_class: type[_T]) -> _T:
9595

9696
prefix = settings_class.model_config.get("env_prefix", "")
9797
for error in errors:
98-
env_var = f"{prefix}{error['loc'][0]}".upper() if error["loc"] else prefix.rstrip("_").upper()
98+
if error["loc"] and isinstance(error["loc"][0], str):
99+
env_var = f"{prefix}{error['loc'][0]}".upper()
100+
else:
101+
env_var = prefix.rstrip("_").upper()
99102
text.append(f"• {env_var}", style="yellow bold")
100103
text.append(f": {error['msg']}\n")
101104

tests/aignostics_foundry_core/settings_test.py

Lines changed: 27 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,7 @@
66
from unittest.mock import MagicMock, patch
77

88
import pytest
9-
from pydantic import SecretStr
9+
from pydantic import RootModel, SecretStr, ValidationError
1010
from pydantic_settings import SettingsConfigDict
1111

1212
from aignostics_foundry_core.settings import (
@@ -155,3 +155,29 @@ def test_load_settings_validation_error_exits(self, mock_console_print: MagicMoc
155155
assert mock_console_print.call_count == 1
156156
panel_arg = mock_console_print.call_args[0][0]
157157
assert isinstance(panel_arg, Panel)
158+
159+
@pytest.mark.unit
160+
@patch("sys.exit")
161+
@patch("aignostics_foundry_core.settings.console.print")
162+
def test_load_settings_validation_error_integer_loc(
163+
self, mock_console_print: MagicMock, mock_exit: MagicMock
164+
) -> None:
165+
"""Test that integer loc[0] falls back to the model prefix instead of "PREFIX_0"."""
166+
# RootModel[list[int]] produces loc=(0,) where loc[0] is an integer
167+
int_loc_error: ValidationError | None = None
168+
try:
169+
RootModel[list[int]].model_validate(["not_an_int"])
170+
except ValidationError as e:
171+
int_loc_error = e
172+
173+
assert int_loc_error is not None
174+
assert isinstance(int_loc_error.errors()[0]["loc"][0], int)
175+
176+
with patch.object(_TheTestSettingsWithEnvPrefix, "__new__", side_effect=int_loc_error):
177+
load_settings(_TheTestSettingsWithEnvPrefix)
178+
179+
mock_exit.assert_called_once_with(78)
180+
panel_arg = mock_console_print.call_args[0][0]
181+
panel_text = str(panel_arg.renderable)
182+
assert "TEST_0" not in panel_text
183+
assert "• TEST:" in panel_text

0 commit comments

Comments
 (0)