ZhuLinsen/daily_stock_analysis · error · ValueError
{field_name} must be one of {allowed_text}
Error message
{field_name} must be one of {allowed_text} What it means
_normalize_enum stringifies, strips, and checks membership in the allowed set; failures raise ValueError '<field> must be one of <sorted allowed list>'. It validates enum-ish params (action, market, source_type, statuses, horizon) on decision-signal outcome APIs.
Source
Thrown at src/services/decision_signal_outcome_service.py:642
@staticmethod
def _optional_positive_int(value: Any, field_name: str) -> Optional[int]:
if value in (None, ""):
return None
try:
number = int(value)
except (TypeError, ValueError) as exc:
raise ValueError(f"{field_name} must be an integer") from exc
if number <= 0:
raise ValueError(f"{field_name} must be positive")
return number
@staticmethod
def _normalize_enum(value: Any, allowed: Iterable[str], field_name: str) -> str:
text = str(value or "").strip()
allowed_set = set(allowed)
if text not in allowed_set:
allowed_text = ", ".join(sorted(allowed_set))
raise ValueError(f"{field_name} must be one of {allowed_text}")
return text
@classmethod
def _normalize_optional_enum(cls, value: Any, allowed: Iterable[str], field_name: str) -> Optional[str]:
if value in (None, ""):
return None
return cls._normalize_enum(value, allowed, field_name)
def _normalize_horizons(self, values: Optional[List[str]]) -> Optional[List[str]]:
if not values:
return None
out: List[str] = []
for value in values:
horizon = self._normalize_enum(value, HORIZONS, "horizon")
if horizon not in out:
out.append(horizon)
return out
View on GitHub (pinned to 5159bd72e8)
Solutions
- Read the error message: it lists the exact allowed values sorted; use one verbatim.
- Source allowed values from the backend (SUPPORTED_OUTCOME_HORIZONS, normalize_decision_action's accepted set) instead of hardcoding.
- Trim/case-normalize user input before sending, but do not invent synonyms.
Example fix
# before service.evaluate_outcomes(action="accumulate", market="china") # after service.evaluate_outcomes(action="buy", market="ashare")
Defensive patterns
Strategy: type-guard
Validate before calling
ALLOWED_ACTIONS = {"buy", "sell", "hold"} # keep in sync with backend
action = (raw_action or "").strip().lower()
if action and action not in ALLOWED_ACTIONS:
raise HTTPException(400, f"action must be one of {sorted(ALLOWED_ACTIONS)}")
service.evaluate_outcomes(action=action or None) Type guard
def isAllowedEnum(value: object, allowed: set[str]) -> bool:
text = str(value or "").strip()
return not text or text in allowed Try / catch
try:
result = service.evaluate_outcomes(action=action, market=market)
except ValueError as exc:
if "must be one of" in str(exc):
return JSONResponse(status_code=400, content={"error": "invalid_enum", "message": str(exc)})
raise Prevention
- Derive allowed values from backend constants (SUPPORTED_OUTCOME_HORIZONS etc.), not hardcoded copies.
- Use select inputs, not free text, for enum fields.
- Add contract tests that fail when backend enums change.
When it happens
Trigger: Passing action="buy" when the allowed set uses "buy"/"sell"/"hold" variants that differ (e.g. "BUY " ok, "accumulate" not), market="cn" vs allowed "ashare"/"hk"/"us", or horizon="1mo" when only defined horizon keys are accepted.
Common situations: Client enum drift after a backend adds/renames allowed values; casing/whitespace differences usually survive normalization but synonyms do not; hardcoded enums in frontend out of sync with SUPPORTED_OUTCOME_HORIZONS / decision action sets.
Related errors
- {field_name} must be an integer
- {field_name} must be positive
- {field_name} must be at most {max_length} characters
- eval_window_days must be a positive integer
- analysis_phase must be one of premarket, intraday, postmarke
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/44652b7c48788f28.
Report an issue: GitHub.