ZhuLinsen/daily_stock_analysis · error · ValueError

invalid 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.

Solutions

  1. Set direction to 'above' or 'below' for price_cross rules (or omit it to default to 'above').
  2. When converting rule types between price_cross and price_change_percent, remap direction vocabulary ('up'->'above', 'down'->'below').
  3. 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

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


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/107723346b503690. Report an issue: GitHub.

Appendix: 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)