{"record":{"id":"d82639d02f164dca","repo":"ZhuLinsen/daily_stock_analysis","slug":"event-alert-rules-list-must-contain-only-objects","errorCode":null,"errorMessage":"Event alert rules list must contain only objects; invalid entries at positions: {invalid_indices}","messagePattern":"Event alert rules list must contain only objects; invalid entries at positions: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/agent/events.py","lineNumber":455,"sourceCode":"    if raw_rules is None:\n        return []\n\n    parsed = raw_rules\n    if isinstance(raw_rules, str):\n        cleaned = raw_rules.strip()\n        if not cleaned:\n            return []\n        parsed = json.loads(cleaned)\n\n    if isinstance(parsed, dict):\n        parsed = parsed.get(\"rules\", [])\n\n    if not isinstance(parsed, list):\n        raise ValueError(\"Event alert rules must be a JSON array\")\n\n    invalid_indices = [idx for idx, entry in enumerate(parsed) if not isinstance(entry, dict)]\n    if invalid_indices:\n        raise ValueError(\n            \"Event alert rules list must contain only objects; \"\n            f\"invalid entries at positions: {invalid_indices}\"\n        )\n\n    return parsed\n\n\ndef validate_event_alert_rule(rule: Dict[str, Any]) -> None:\n    \"\"\"Validate one serialized EventMonitor rule.\"\"\"\n    if not isinstance(rule, dict):\n        raise ValueError(\"Event alert rule must be an object\")\n\n    stock_code = str(rule.get(\"stock_code\") or \"\").strip()\n    if not stock_code:\n        raise ValueError(\"stock_code is required\")\n\n    try:\n        alert_type = AlertType(rule.get(\"alert_type\", \"\"))","sourceCodeStart":437,"sourceCodeEnd":473,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/agent/events.py#L437-L473","documentation":"After the rules payload is confirmed to be a list ([237]), the parser checks every element with isinstance(entry, dict). Any non-object element (string, number, nested array, null, bool) makes it raise ValueError listing the exact zero-based positions of the invalid entries, so users can locate the offenders without guessing.","triggerScenarios":"A rules array like [\"alert\", 42, null, {...}] — mixing raw values with rule objects; commonly from JSON where a rule was replaced by its ID, a name, or omitted fields collapsed to a scalar.","commonSituations":"Frontends sending [{id: 1}, {id: 2}] references instead of full rule objects; LLM-generated rule JSON emitting strings; copy-paste of stock codes directly into the array.","solutions":["Remove or replace the scalar entries at the reported positions with full rule objects","If the client meant to send identifiers, resolve them to full rules server-side first","Map over the array at the producer to enforce object-ness before submit","Return the error message verbatim to the client — the positions are actionable"],"exampleFix":"// before\n[{\"stock_code\": \"600519\"}, \"AAPL\", null]\n\n// after\n[{\"stock_code\": \"600519\"}, {\"stock_code\": \"AAPL\", \"alert_type\": \"price_cross\", \"price\": 200}]","handlingStrategy":"validation","validationCode":"invalid = [i for i, e in enumerate(rules) if not isinstance(e, dict)]\nif invalid:\n    raise ValueError(f\"non-object rule entries at {invalid}\")","typeGuard":"def all_rules_are_objects(rules: list) -> bool:\n    return all(isinstance(e, dict) for e in rules)","tryCatchPattern":null,"preventionTips":["Always emit objects in rule arrays","Surface the positions list to users","Validate client-side before submit"],"tags":["python","json","validation","alerts"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}