{"record":{"id":"3a1404bfc303120b","repo":"ZhuLinsen/daily_stock_analysis","slug":"unsupported-alert-type-alert-type","errorCode":null,"errorMessage":"unsupported alert_type: {alert_type}","messagePattern":"unsupported alert_type: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"warning","filePath":"src/agent/events.py","lineNumber":421,"sourceCode":"                if alert_type == AlertType.PRICE_CROSS.value:\n                    rule = PriceAlert(\n                        stock_code=stock_code,\n                        direction=entry.get(\"direction\", \"above\").lower(),\n                        price=float(entry.get(\"price\", 0.0)),\n                    )\n                elif alert_type == AlertType.PRICE_CHANGE_PERCENT.value:\n                    rule = PriceChangeAlert(\n                        stock_code=stock_code,\n                        direction=entry.get(\"direction\", \"up\").lower(),\n                        change_pct=float(entry[\"change_pct\"]),\n                    )\n                elif alert_type == AlertType.VOLUME_SPIKE.value:\n                    rule = VolumeAlert(\n                        stock_code=stock_code,\n                        multiplier=float(entry.get(\"multiplier\", 2.0)),\n                    )\n                else:\n                    raise ValueError(f\"unsupported alert_type: {alert_type}\")\n                rule.status = AlertStatus(entry.get(\"status\", \"active\"))\n                raw_created = entry.get(\"created_at\")\n                try:\n                    rule.created_at = float(raw_created) if raw_created is not None else time.time()\n                except (TypeError, ValueError):\n                    rule.created_at = time.time()\n                rule.ttl_hours = float(entry.get(\"ttl_hours\", 24.0))\n                monitor.add_alert(rule)\n            except Exception as exc:\n                logger.warning(\"[EventMonitor] Skip invalid rule #%d: %s\", index, exc)\n        return monitor\n\n\ndef parse_event_alert_rules(raw_rules: Any) -> List[Dict[str, Any]]:\n    \"\"\"Parse event alert rules from config JSON or already-loaded objects.\"\"\"\n    if raw_rules is None:\n        return []\n","sourceCodeStart":403,"sourceCodeEnd":439,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/agent/events.py#L403-L439","documentation":"While deserializing stored monitor state (from_json-ish loop around events.py:403-421), each entry's alert_type string is matched against the three implemented values; an unknown value falls to the else branch and raises ValueError('unsupported alert_type: ...'). The surrounding loop catches Exception per entry and logs 'Skip invalid rule #N', so one bad rule does not kill the whole monitor load.","triggerScenarios":"Restoring an EventMonitor from a saved rules array containing an alert_type string outside {price_cross, price_change_percent, volume_spike} — e.g. a typo, a value from a newer/older schema, or an unimplemented enum member stored earlier.","commonSituations":"Migrating between versions where alert types were added; hand-edited rule files; a producer writing alert_type as the enum's Python name ('PRICE_CROSS') instead of its value ('price_cross').","solutions":["Fix or remove the offending entry in the stored rules JSON; use lowercase values: price_cross, price_change_percent, volume_spike","If rules come from another system, normalize alert_type to lowercase values before saving","When adding new alert types, extend both the loop here and _RUNTIME_SUPPORTED_ALERT_TYPES, then migrate stored data","Check logs for '[EventMonitor] Skip invalid rule #N' to find which index was dropped"],"exampleFix":"// before\n{\"rules\": [{\"stock_code\": \"AAPL\", \"alert_type\": \"PRICE_CROSS\", \"price\": 100}]}\n\n// after\n{\"rules\": [{\"stock_code\": \"AAPL\", \"alert_type\": \"price_cross\", \"price\": 100, \"direction\": \"above\"}]}","handlingStrategy":"validation","validationCode":"SUPPORTED = {\"price_cross\", \"price_change_percent\", \"volume_spike\"}\n\nrules = [r for r in stored_rules if r.get(\"alert_type\") in SUPPORTED]\n# log dropped ones explicitly","typeGuard":"def is_serializable_alert_rule(entry: dict) -> bool:\n    return isinstance(entry, dict) and entry.get(\"alert_type\") in {\n        \"price_cross\", \"price_change_percent\", \"volume_spike\"\n    }","tryCatchPattern":null,"preventionTips":["Normalize alert_type casing at write time","Validate rules before persisting","Watch the 'Skip invalid rule' log line"],"tags":["python","alerts","deserialization","event-monitor"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}