ZhuLinsen/daily_stock_analysis · error · ValueError
invalid direction: {direction}
Error message
invalid direction: {direction} What it means
Raised for PRICE_CROSS rules whose 'direction' field is not 'above' or 'below' (case-insensitive after .lower(), default 'above'). A price-cross alert needs to know whether it fires when price crosses above or below the threshold, so any other word makes the rule unexecutable.
Source
Thrown at src/agent/events.py:497
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:
raise ValueError(f"invalid change_pct: {rule.get('change_pct')}") from exc
if change_pct <= 0:
raise ValueError("change_pct must be > 0")
elif alert_type == AlertType.VOLUME_SPIKE:
try:View on GitHub (pinned to 5159bd72e8)
Solutions
- Set direction to 'above' or 'below' for price_cross rules (or omit it to default to 'above').
- When converting rule types between price_cross and price_change_percent, remap direction vocabulary ('up'->'above', 'down'->'below').
- Add an enum check in the rule-building UI/LLM extractor for the direction field per alert type.
Example fix
// before
rule = {"stock_code": "600519", "alert_type": "price_cross", "direction": "up", "price": 1800}
# ValueError: invalid direction: up
// after
rule = {"stock_code": "600519", "alert_type": "price_cross", "direction": "above", "price": 1800}
validate_event_alert_rule(rule) Defensive patterns
Strategy: validation
Validate before calling
def valid_cross_direction(raw) -> bool:
return str(raw if raw is not None else "above").lower() in {"above", "below"} Type guard
def is_valid_price_cross_rule(rule: dict) -> bool:
return (
str(rule.get("alert_type", "")).lower() == "price_cross"
and valid_cross_direction(rule.get("direction"))
) Try / catch
try:
validate_event_alert_rule(rule)
except ValueError as e:
if str(e).startswith("invalid direction"):
rule["direction"] = "above" if rule.get("direction") in ("up", "higher") else "below"
validate_event_alert_rule(rule) Prevention
- Remember the vocabulary per type: price_cross=above/below, price_change_percent=up/down.
- When converting rule types, remap direction too.
- Omit direction to accept the default instead of guessing.
When it happens
Trigger: validate_event_alert_rule with alert_type='price_cross' and direction set to 'up', 'down', 'higher', '>', 'greater', or '' (empty string fails; only omission is safe and defaults to 'above'). Values are lowercased first, so 'Above' passes.
Common situations: Confusion with PRICE_CHANGE_PERCENT which uses 'up'/'down' vocabulary; LLM or user writing 'crosses above' prose; copying a PRICE_CHANGE_PERCENT rule template and only changing alert_type.
Related errors
- unsupported alert_type for current EventMonitor runtime: {al
- stock_code is required
- invalid alert_type: {rule.get('alert_type')}
- invalid status: {status}
- invalid ttl_hours: {ttl_hours}
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/107723346b503690.
Report an issue: GitHub.