{"record":{"id":"2f19150d74db5f51","repo":"ZhuLinsen/daily_stock_analysis","slug":"unsupported-alert-type","errorCode":"unsupported_alert_type","errorMessage":"unsupported alert_type for Alert API: {alert_type or '<empty>'}","messagePattern":"unsupported alert_type for Alert API: (.+?)","errorType":"http","errorClass":"UnsupportedAlertTypeError","httpStatus":400,"severity":"error","filePath":"src/services/alert_service.py","lineNumber":881,"sourceCode":"        return {\n            \"items\": [self._serialize_notification(row) for row in rows],\n            \"total\": total,\n            \"page\": page,\n            \"page_size\": page_size,\n        }\n\n    def _normalize_rule_payload(self, payload: Dict[str, Any], *, source: str = \"api\") -> Dict[str, Any]:\n        target_scope = str(payload.get(\"target_scope\") or \"single_symbol\").strip()\n        if target_scope not in SUPPORTED_TARGET_SCOPES:\n            raise AlertServiceError(f\"unsupported target_scope: {target_scope}\")\n\n        target = str(payload.get(\"target\") or \"\").strip()\n        if not target:\n            raise AlertServiceError(\"target is required\")\n\n        alert_type = str(payload.get(\"alert_type\") or \"\").strip().lower()\n        if alert_type not in SUPPORTED_ALERT_TYPES:\n            raise UnsupportedAlertTypeError(f\"unsupported alert_type for Alert API: {alert_type or '<empty>'}\")\n        self._validate_scope_alert_type(target_scope, alert_type)\n\n        severity = str(payload.get(\"severity\") or \"warning\").strip().lower()\n        if severity not in SUPPORTED_SEVERITIES:\n            raise AlertServiceError(f\"unsupported severity: {severity}\")\n\n        parameters = self._normalize_parameters(alert_type, payload.get(\"parameters\") or {})\n        target = self._normalize_target(target_scope, target)\n        if target_scope == \"single_symbol\" and alert_type in LEGACY_RUNTIME_ALERT_TYPES:\n            serialized_rule = {\"stock_code\": target, \"alert_type\": alert_type, **parameters}\n            try:\n                validate_event_alert_rule(serialized_rule)\n            except ValueError as exc:\n                raise AlertServiceError(str(exc)) from exc\n\n        name = str(payload.get(\"name\") or \"\").strip()\n        if not name:\n            name = self._default_rule_name(target=target, alert_type=alert_type, parameters=parameters)","sourceCodeStart":863,"sourceCodeEnd":899,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/services/alert_service.py#L863-L899","documentation":"UnsupportedAlertTypeError (subclass of AlertServiceError, code unsupported_alert_type) raised in _normalize_rule_payload when alert_type — after lowercasing and stripping — is not in SUPPORTED_ALERT_TYPES (the union of symbol, portfolio, and market alert type sets). This runs before scope/type compatibility checks.","triggerScenarios":"Rule create/update with alert_type missing (message shows '<empty>'), misspelled ('price_crossing', 'volume-spike'), or a type the backend version does not support yet.","commonSituations":"Frontend deployed with new alert types ahead of the backend; typos in hand-written payloads; deprecated alert type renamed in a newer release; empty alert_type because the form default was never set.","solutions":["Check SUPPORTED_ALERT_TYPES in src/services/alert_service.py (line 77) for the exact set your backend accepts.","Fix the spelling/casing — 'PRICE_CROSS' is fine (lowercased), 'price_crossing' is not.","If the type is genuinely new, upgrade the backend first, then enable it in the client."],"exampleFix":"// before\n{\"alert_type\": \"price_threshold\", ...}\n\n// after\n{\"alert_type\": \"price_cross\", ...}","handlingStrategy":"type-guard","validationCode":"from src.services.alert_service import SUPPORTED_ALERT_TYPES\n\nalert_type = str(payload.get(\"alert_type\") or \"\").strip().lower()\nif alert_type not in SUPPORTED_ALERT_TYPES:\n    raise HTTPException(status_code=400, detail=f\"unsupported alert_type: {alert_type}\")","typeGuard":"import { SUPPORTED_ALERT_TYPES } from \"./alertTypes\"; // keep in sync with backend\nexport function isSupportedAlertType(v: unknown): v is string {\n  return typeof v === \"string\" && SUPPORTED_ALERT_TYPES.includes(v.toLowerCase());\n}","tryCatchPattern":"from src.services.alert_service import AlertServiceError, UnsupportedAlertTypeError\n\ntry:\n    alert_service.create_rule(payload)\nexcept UnsupportedAlertTypeError as exc:\n    return JSONResponse(status_code=422, content={\"detail\": str(exc)})","preventionTips":["Source the alert-type list from the backend constants or a versioned API response instead of duplicating it.","Deploy backend support for a new alert type before enabling it in any client.","Always send an explicit alert_type; never rely on empty defaults."],"tags":["alerts","validation","enum","alert-type"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}