ZhuLinsen/daily_stock_analysis · error · ValueError
ttl_hours must be > 0
Error message
ttl_hours must be > 0
What it means
Raised when 'ttl_hours' parses as a float but is zero or negative. A non-positive TTL would make a rule expire immediately or in the past, which is meaningless for an alert monitor, so validation rejects it up front rather than silently creating a dead rule.
Source
Thrown at src/agent/events.py:492
except ValueError as exc:
raise ValueError(f"invalid alert_type: {rule.get('alert_type')}") from exc
_ensure_runtime_supported_alert_type(alert_type)
status = rule.get("status")
if status is not None:
try:
AlertStatus(status)
except ValueError as exc:
raise ValueError(f"invalid status: {status}") from exc
ttl_hours = rule.get("ttl_hours")
if ttl_hours is not None:
try:
ttl_value = float(ttl_hours)
except (TypeError, ValueError) as exc:
raise ValueError(f"invalid ttl_hours: {ttl_hours}") from exc
if ttl_value <= 0:
raise ValueError("ttl_hours must be > 0")
if alert_type == AlertType.PRICE_CROSS:
direction = str(rule.get("direction", "above")).lower()
if direction not in {"above", "below"}:
raise ValueError(f"invalid direction: {direction}")
try:
price = float(rule.get("price"))
except (TypeError, ValueError) as exc:
raise ValueError(f"invalid price: {rule.get('price')}") from exc
if price <= 0:
raise ValueError("price must be > 0")
elif alert_type == AlertType.PRICE_CHANGE_PERCENT:
direction = str(rule.get("direction", "up")).lower()
if direction not in {"up", "down"}:
raise ValueError(f"invalid direction: {direction}")
try:
change_pct = float(rule.get("change_pct"))
except (TypeError, ValueError) as exc:View on GitHub (pinned to 5159bd72e8)
Solutions
- Set ttl_hours to a positive number of hours (e.g. 24).
- If 'no expiry' was intended, check whether the runtime supports omitting ttl_hours instead of using 0/-1 sentinels.
- Guard computed TTLs: clamp or reject when the computed value is <= 0 before submitting the rule.
Example fix
// before
rule = {"stock_code": "AAPL", "alert_type": "volume_spike", "ttl_hours": -1}
# ValueError: ttl_hours must be > 0
// after
rule = {"stock_code": "AAPL", "alert_type": "volume_spike", "ttl_hours": 24}
validate_event_alert_rule(rule) Defensive patterns
Strategy: validation
Validate before calling
def positive_ttl(raw) -> bool:
try:
return raw is None or float(raw) > 0
except (TypeError, ValueError):
return False Type guard
def has_positive_ttl(rule: dict) -> bool:
return positive_ttl(rule.get("ttl_hours")) Try / catch
try:
validate_event_alert_rule(rule)
except ValueError as e:
if "ttl_hours must be" in str(e):
rule["ttl_hours"] = DEFAULT_TTL_HOURS # or reject Prevention
- Never use 0/-1 sentinels for 'no expiry'.
- Sanity-check computed TTLs (expiry - now) before submission.
- Default UI numeric inputs to a real TTL like 24, not 0.
When it happens
Trigger: validate_event_alert_rule with ttl_hours = 0, -1, 0.0, '-0.5', or '-24'. NaN technically slips past float() but 0/negative are the concrete triggers; boundary values like 1e-9 pass but are dubious.
Common situations: UI defaulting an unset numeric input to 0; arithmetic that computes ttl_hours = expiry - now and goes negative when expiry is in the past; sentinel values like -1 reused from another config schema meaning 'infinite'.
Related errors
- invalid ttl_hours: {ttl_hours}
- price must be > 0
- unsupported alert_type for current EventMonitor runtime: {al
- stock_code is required
- invalid alert_type: {rule.get('alert_type')}
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/96f2c1c99a4b59ec.
Report an issue: GitHub.