fix(config): coerce none/null empty for Optional env fields

Closes #1899.

None-sentinel coercion for Optional[str] was unreachable because the
str converter never raises and ran before the NoneType branch.

(cherry picked from commit 02b8aebe33)
This commit is contained in:
Bartok9
2026-07-14 02:01:20 -04:00
committed by Assaf Elovic
parent 9038ffa3fb
commit da6ecbb7ca
2 changed files with 36 additions and 8 deletions
+11 -8
View File
@@ -260,16 +260,19 @@ class Config:
args = get_args(type_hint)
if origin is Union:
# Handle Union types (e.g., Union[str, None])
# Handle Union types (e.g., Union[str, None] / Optional[str]).
# Check the None sentinel BEFORE non-None args: for Optional[str],
# str conversion never raises, so looping str-first permanently
# shadowed the none/null/"" → None branch (see issue #1899).
if type(None) in args and env_value.lower() in ("none", "null", ""):
return None
for arg in args:
if arg is type(None):
if env_value.lower() in ("none", "null", ""):
return None
else:
try:
return Config.convert_env_value(key, env_value, arg)
except ValueError:
continue
continue
try:
return Config.convert_env_value(key, env_value, arg)
except ValueError:
continue
raise ValueError(f"Cannot convert {env_value} to any of {args}")
if type_hint is bool:
+25
View File
@@ -0,0 +1,25 @@
"""Regression tests for Optional[str] env coercion (issue #1899)."""
from typing import Optional, Union
import pytest
from gpt_researcher.config.config import Config
@pytest.mark.parametrize(
"raw",
["none", "null", "", "NONE", "Null"],
)
def test_optional_str_coerces_none_sentinels(raw):
assert Config.convert_env_value("AGENT_ROLE", raw, Union[str, None]) is None
assert Config.convert_env_value("AGENT_ROLE", raw, Optional[str]) is None
def test_optional_str_preserves_real_values():
assert Config.convert_env_value("AGENT_ROLE", "researcher", Optional[str]) == "researcher"
assert Config.convert_env_value("AGENT_ROLE", "0", Union[str, None]) == "0"
def test_optional_int_still_coerces_and_parses():
assert Config.convert_env_value("SEED", "none", Optional[int]) is None
assert Config.convert_env_value("SEED", "42", Optional[int]) == 42