ZhuLinsen/daily_stock_analysis · error · ValueError

Event alert rules must be a JSON array

Error message

Event alert rules must be a JSON array

What it means

The rules-parsing helper accepts raw rules as: a JSON string (parsed with json.loads, empty string -> []), a dict (unwrapped via its 'rules' key), or an already-parsed list. After that, anything that is still not a list — a bare scalar, a JSON string encoding an object without 'rules', a top-level JSON number/bool — raises ValueError('Event alert rules must be a JSON array').

Source

Thrown at src/agent/events.py:451


def parse_event_alert_rules(raw_rules: Any) -> List[Dict[str, Any]]:
    """Parse event alert rules from config JSON or already-loaded objects."""
    if raw_rules is None:
        return []

    parsed = raw_rules
    if isinstance(raw_rules, str):
        cleaned = raw_rules.strip()
        if not cleaned:
            return []
        parsed = json.loads(cleaned)

    if isinstance(parsed, dict):
        parsed = parsed.get("rules", [])

    if not isinstance(parsed, list):
        raise ValueError("Event alert rules must be a JSON array")

    invalid_indices = [idx for idx, entry in enumerate(parsed) if not isinstance(entry, dict)]
    if invalid_indices:
        raise ValueError(
            "Event alert rules list must contain only objects; "
            f"invalid entries at positions: {invalid_indices}"
        )

    return parsed


def validate_event_alert_rule(rule: Dict[str, Any]) -> None:
    """Validate one serialized EventMonitor rule."""
    if not isinstance(rule, dict):
        raise ValueError("Event alert rule must be an object")

    stock_code = str(rule.get("stock_code") or "").strip()
    if not stock_code:

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Ensure the payload's top level is an array of rule objects: [{...}, {...}]
  2. If you send an object, it must be exactly {"rules": [...]} — the parser unwraps only that key
  3. Validate with the parser's own contract (call it inside try/except) before persisting, and return a 400 to the client on ValueError
  4. Log the offending payload shape (not contents) to catch producer-side format drift

Example fix

// before
raw = '{"600519": {"alert_type": "price_cross"}}'  # dict without "rules"
parse_rules(raw)  # ValueError

// after
raw = '{"rules": [{"stock_code": "600519", "alert_type": "price_cross", "price": 1800}]}'
Defensive patterns

Strategy: validation

Validate before calling

import json

def rules_payload_ok(raw) -> bool:
    parsed = json.loads(raw) if isinstance(raw, str) else raw
    if isinstance(parsed, dict):
        parsed = parsed.get("rules")
    return isinstance(parsed, list)

Type guard

import json

def is_rules_array(raw) -> bool:
    try:
        parsed = json.loads(raw) if isinstance(raw, str) else raw
    except json.JSONDecodeError:
        return False
    if isinstance(parsed, dict):
        parsed = parsed.get("rules")
    return isinstance(parsed, list)

Try / catch

except ValueError as exc:
    if "must be a JSON array" in str(exc):
        return HTTP400(f"rules must be a JSON array: {exc}")

Prevention

When it happens

Trigger: Passing raw_rules='42', 'true', '"alert"', a dict without a 'rules' key, or a list-of-lists-free scalar to the parser; also JSON text whose top level is not an array or a {"rules": [...]} wrapper.

Common situations: API endpoints forwarding arbitrary user JSON; env vars holding malformed rule blobs; frontend sending an object keyed by stock code instead of an array; trailing commas making json.loads return a non-container.

Related errors


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