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

  1. Read the error message: it lists the exact allowed values sorted; use one verbatim.
  2. Source allowed values from the backend (SUPPORTED_OUTCOME_HORIZONS, normalize_decision_action's accepted set) instead of hardcoding.
  3. 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

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


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/44652b7c48788f28. Report an issue: GitHub.