ZhuLinsen/daily_stock_analysis · error · ValueError

invalid direction: {direction}

Error message

invalid direction: {direction}

What it means

Raised by _direction (src/services/alert_indicators.py:371) when the normalized direction string (stripped, lowercased) is not in the allowed set for that alert type: ABOVE_BELOW_DIRECTIONS (above/below) for ma_price_cross, rsi_threshold, cci_threshold, or CROSS_DIRECTIONS (bullish_cross/bearish_cross) for macd_cross, kdj_cross. Note falsy values (None, '') fall back to the per-type default, so only non-empty wrong strings trigger it.

Source

Thrown at src/services/alert_indicators.py:371

        message=message,
        data_timestamp=latest,
    )


def _ensure_required_bars_fetchable(alert_type: str, params: Dict[str, Any]) -> Dict[str, Any]:
    required_bars = compute_required_bars(alert_type, params)
    if required_bars > MAX_REQUESTED_DAYS:
        raise ValueError(
            f"{alert_type} periods require {required_bars} bars, "
            f"but at most {MAX_REQUESTED_DAYS} days can be requested"
        )
    return params


def _direction(value: Any, allowed: frozenset[str], *, default: str) -> str:
    direction = str(value or default).strip().lower()
    if direction not in allowed:
        raise ValueError(f"invalid direction: {direction}")
    return direction


def _int_in_range(value: Any, field_name: str, *, default: int, minimum: int = 2, maximum: int = 250) -> int:
    raw_value = default if value is None or value == "" else value
    try:
        number = int(raw_value)
    except (TypeError, ValueError) as exc:
        raise ValueError(f"invalid {field_name}: {value}") from exc
    if str(raw_value).strip() not in {str(number), f"{number}.0"}:
        raise ValueError(f"{field_name} must be an integer")
    if number < minimum or number > maximum:
        raise ValueError(f"{field_name} must be between {minimum} and {maximum}")
    return number


def _finite_float(value: Any, field_name: str) -> float:
    try:

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Use 'above'/'below' for ma_price_cross, rsi_threshold, cci_threshold and 'bullish_cross'/'bearish_cross' for macd_cross, kdj_cross.
  2. Omit direction entirely to take the per-type default (above / bullish_cross) when you do not need it.
  3. Fix the frontend enum/dropdown to mirror the backend sets exactly.

Example fix

// before
{ "alert_type": "macd_cross", "parameters": { "direction": "golden_cross" } }

// after
{ "alert_type": "macd_cross", "parameters": { "direction": "bullish_cross" } }
Defensive patterns

Strategy: type-guard

Validate before calling

DIRECTIONS = {
    'ma_price_cross': {'above','below'}, 'rsi_threshold': {'above','below'}, 'cci_threshold': {'above','below'},
    'macd_cross': {'bullish_cross','bearish_cross'}, 'kdj_cross': {'bullish_cross','bearish_cross'},
}
d = str(params.get('direction') or '').strip().lower()
if d and d not in DIRECTIONS[alert_type]:
    raise ValueError(f'direction must be one of {sorted(DIRECTIONS[alert_type])}')

Type guard

def is_valid_direction(alert_type: str, direction) -> bool:
    allowed = {'above','below'} if alert_type in ('ma_price_cross','rsi_threshold','cci_threshold') else {'bullish_cross','bearish_cross'}
    d = str(direction or '').strip().lower()
    return d in allowed or d == ''  # '' falls back to default

Try / catch

try:
    normalize_indicator_parameters(alert_type, params)
except ValueError as e:
    if str(e).startswith('invalid direction:'):
        return bad_request(f'direction {params.get("direction")!r} not valid for {alert_type}')
    raise

Prevention

When it happens

Trigger: Sending direction: "up" to ma_price_cross (allowed: above|below); "bullish" or "golden_cross" to macd_cross (allowed: bullish_cross|bearish_cross); "Both" or "cross" to rsi_threshold. Case and whitespace are tolerated, wrong vocabulary is not.

Common situations: UI dropdown values drifting from backend enum ("up"/"down" vs "above"/"below"); mixing oscillator vocabulary with cross vocabulary between alert types; docs examples using shorthand.

Related errors


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