{"record":{"id":"df9fedec79388f02","repo":"ZhuLinsen/daily_stock_analysis","slug":"price-must-be-0","errorCode":null,"errorMessage":"price must be > 0","messagePattern":"price must be > 0","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/agent/events.py","lineNumber":503,"sourceCode":"    ttl_hours = rule.get(\"ttl_hours\")\n    if ttl_hours is not None:\n        try:\n            ttl_value = float(ttl_hours)\n        except (TypeError, ValueError) as exc:\n            raise ValueError(f\"invalid ttl_hours: {ttl_hours}\") from exc\n        if ttl_value <= 0:\n            raise ValueError(\"ttl_hours must be > 0\")\n\n    if alert_type == AlertType.PRICE_CROSS:\n        direction = str(rule.get(\"direction\", \"above\")).lower()\n        if direction not in {\"above\", \"below\"}:\n            raise ValueError(f\"invalid direction: {direction}\")\n        try:\n            price = float(rule.get(\"price\"))\n        except (TypeError, ValueError) as exc:\n            raise ValueError(f\"invalid price: {rule.get('price')}\") from exc\n        if price <= 0:\n            raise ValueError(\"price must be > 0\")\n    elif alert_type == AlertType.PRICE_CHANGE_PERCENT:\n        direction = str(rule.get(\"direction\", \"up\")).lower()\n        if direction not in {\"up\", \"down\"}:\n            raise ValueError(f\"invalid direction: {direction}\")\n        try:\n            change_pct = float(rule.get(\"change_pct\"))\n        except (TypeError, ValueError) as exc:\n            raise ValueError(f\"invalid change_pct: {rule.get('change_pct')}\") from exc\n        if change_pct <= 0:\n            raise ValueError(\"change_pct must be > 0\")\n    elif alert_type == AlertType.VOLUME_SPIKE:\n        try:\n            multiplier = float(rule.get(\"multiplier\", 2.0))\n        except (TypeError, ValueError) as exc:\n            raise ValueError(f\"invalid multiplier: {rule.get('multiplier')}\") from exc\n        if multiplier <= 0:\n            raise ValueError(\"multiplier must be > 0\")\n","sourceCodeStart":485,"sourceCodeEnd":521,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/agent/events.py#L485-L521","documentation":"Raised for PRICE_CROSS rules when 'price' parses as a float but is <= 0. A zero or negative threshold is meaningless for a stock price cross, so validation rejects it before the rule can enter the monitor and never trigger (or always mis-trigger).","triggerScenarios":"validate_event_alert_rule with alert_type='price_cross' and price = 0, -50, 0.0, or '0'. Also reachable when unparsed text coerces unexpectedly (e.g. float of a string like '0').","commonSituations":"Unset numeric UI field defaulting to 0; an LLM emitting 0 as a placeholder for 'current price'; negative values from buggy delta/threshold arithmetic being reused as absolute price; sentinel -1 reused from a different config schema.","solutions":["Set price to the positive absolute price threshold you want (e.g. 1800.0).","If you meant a percentage move, use alert_type='price_change_percent' with change_pct instead of price.","Validate extracted prices at the LLM-parsing layer: reject 0/negative placeholders and re-ask or fall back to the current quote."],"exampleFix":"// before\nrule = {\"stock_code\": \"600519\", \"alert_type\": \"price_cross\", \"direction\": \"above\", \"price\": 0}\n# ValueError: price must be > 0\n\n// after\nrule = {\"stock_code\": \"600519\", \"alert_type\": \"price_cross\", \"direction\": \"above\", \"price\": 1800.0}\nvalidate_event_alert_rule(rule)","handlingStrategy":"validation","validationCode":"def positive_price(raw) -> bool:\n    try:\n        return float(raw) > 0\n    except (TypeError, ValueError):\n        return False","typeGuard":"def has_positive_price(rule: dict) -> bool:\n    return positive_price(rule.get(\"price\"))","tryCatchPattern":"try:\n    validate_event_alert_rule(rule)\nexcept ValueError as e:\n    if \"price must be\" in str(e):\n        reject(rule, \"price threshold must be a positive absolute price\")","preventionTips":["Reject 0/negative LLM placeholder prices at extraction time.","Use price_change_percent if a relative move was intended.","Guard UI numeric inputs with min>0."],"tags":["validation","event-monitor","price","range-check"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}