ZhuLinsen/daily_stock_analysis · error · ValueError

parameters must be an object

Error message

parameters must be an object

What it means

Raised by normalize_indicator_parameters (src/services/alert_indicators.py:46) when the parameters argument for a technical alert rule is not a Python dict / JSON object. The normalizer expects an object mapping field names (direction, window, period, ...) to values; arrays, strings, numbers, or null all fail this isinstance check before any field validation runs.

Source

Thrown at src/services/alert_indicators.py:46

class TechnicalIndicatorAlert:
    stock_code: str
    alert_type: str
    indicator_params: Dict[str, Any]
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class IndicatorEvaluation:
    status: str
    observed_value: Optional[float]
    threshold: Optional[float]
    message: str
    data_timestamp: Optional[datetime] = None


def normalize_indicator_parameters(alert_type: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
    if not isinstance(parameters, dict):
        raise ValueError("parameters must be an object")

    if alert_type == "ma_price_cross":
        normalized = {
            "direction": _direction(parameters.get("direction"), ABOVE_BELOW_DIRECTIONS, default="above"),
            "window": _int_in_range(parameters.get("window"), "window", default=20),
        }
        return _ensure_required_bars_fetchable(alert_type, normalized)
    if alert_type == "rsi_threshold":
        normalized = {
            "direction": _direction(parameters.get("direction"), ABOVE_BELOW_DIRECTIONS, default="above"),
            "period": _int_in_range(parameters.get("period"), "period", default=12),
            "threshold": _float_in_range(parameters.get("threshold"), "threshold", minimum=0.0, maximum=100.0),
        }
        return _ensure_required_bars_fetchable(alert_type, normalized)
    if alert_type == "macd_cross":
        fast_period = _int_in_range(parameters.get("fast_period"), "fast_period", default=12)
        slow_period = _int_in_range(parameters.get("slow_period"), "slow_period", default=26)
        if fast_period >= slow_period:

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Send parameters as a JSON object: {"parameters": {"direction": "above", "window": 20}}.
  2. If parameters arrive as a JSON string (double-encoded), parse it once with json.loads before calling the API/normalizer.
  3. Add a schema check (JSON Schema / Pydantic model) at the API boundary so non-object payloads get a 422 instead of reaching this ValueError.

Example fix

// before
{ "alert_type": "rsi_threshold", "parameters": "period=14,threshold=70" }

// after
{ "alert_type": "rsi_threshold", "parameters": { "period": 14, "threshold": 70, "direction": "above" } }
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(parameters, dict):
    if isinstance(parameters, str):
        import json; parameters = json.loads(parameters)  # unwrap double-encoded
    else:
        raise ValueError('parameters must be a JSON object')
normalized = normalize_indicator_parameters(alert_type, parameters)

Type guard

def is_indicator_parameters_object(v) -> bool:
    return isinstance(v, dict) and all(isinstance(k, str) for k in v)

Try / catch

try:
    normalize_indicator_parameters(alert_type, parameters)
except ValueError as e:
    if str(e) == 'parameters must be an object':
        return bad_request('parameters must be a JSON object')
    raise

Prevention

When it happens

Trigger: Posting an alert rule (alert_type like ma_price_cross, rsi_threshold, macd_cross, kdj_cross, cci_threshold) with "parameters": [] or "parameters": "window=20" or "parameters": null in the JSON body; or a Python caller passing a list of tuples instead of a dict.

Common situations: Frontend form serializing parameters as a query string instead of an object; JSON schema drift where an older client sends an array; a migration script writing parameters as a JSON-encoded string rather than a parsed object.

Related errors


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