{"record":{"id":"5c20a06e25eddc14","repo":"ZhuLinsen/daily_stock_analysis","slug":"event-alert-rule-must-be-an-object","errorCode":null,"errorMessage":"Event alert rule must be an object","messagePattern":"Event alert rule must be an object","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/agent/events.py","lineNumber":466,"sourceCode":"        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\", \"\"))\n    except ValueError as exc:\n        raise ValueError(f\"invalid alert_type: {rule.get('alert_type')}\") from exc\n    _ensure_runtime_supported_alert_type(alert_type)\n\n    status = rule.get(\"status\")\n    if status is not None:\n        try:\n            AlertStatus(status)\n        except ValueError as exc:\n            raise ValueError(f\"invalid status: {status}\") from exc\n","sourceCodeStart":448,"sourceCodeEnd":484,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/agent/events.py#L448-L484","documentation":"validate_event_alert_rule() is the per-rule validator run after list-level checks. Its first assertion is that the rule is a dict; given Python's dynamic calls, passing a JSON-decoded scalar, an ORM object, or None raises ValueError('Event alert rule must be an object'). Unlike [238] this is the single-rule entry point, so callers looping over untrusted data hit it per item.","triggerScenarios":"Calling validate_event_alert_rule(rule) directly with a non-dict: a string from a form field, a list from a mis-parsed payload, None defaults, or a dataclass not converted via asdict().","commonSituations":"API handlers validating one request body that arrived as a JSON scalar; tests passing dataclasses; config loaders feeding raw TOML/JSON scalars.","solutions":["Convert the input to a plain dict before validating (dataclasses.asdict, model_dump, json round-trip)","Type-check at the API boundary and return 400 for non-object bodies","If looping over user data, pre-filter with isinstance(entry, dict) as [238]'s parser does","Keep one normalization function so all callers pass the same shape"],"exampleFix":"// before\nvalidate_event_alert_rule(request.args.get(\"rule\"))  # str -> ValueError\n\n// after\nimport json\nrule = json.loads(request.get_data())\nif not isinstance(rule, dict):\n    abort(400, \"rule must be a JSON object\")\nvalidate_event_alert_rule(rule)","handlingStrategy":"type-guard","validationCode":"if not isinstance(rule, dict):\n    raise ValueError(\"rule must be a JSON object\")","typeGuard":"def is_rule_object(rule) -> bool:\n    return isinstance(rule, dict)","tryCatchPattern":null,"preventionTips":["Normalize to dict at the boundary","Type-guard before validate","Reject non-object bodies with 400"],"tags":["python","validation","json","alerts"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}