ZhuLinsen/daily_stock_analysis · error · ValueError

invalid status: {status}

Error message

invalid status: {status}

What it means

Raised when an optional 'status' field on an alert rule is present but is not one of the AlertStatus enum values (active, triggered, expired, dismissed). status is optional — omitting it entirely is fine — but once present it must match exactly so rule lifecycle state stays consistent with what the EventMonitor runtime understands.

Source

Thrown at src/agent/events.py:483

    if not isinstance(rule, dict):
        raise ValueError("Event alert rule must be an object")

    stock_code = str(rule.get("stock_code") or "").strip()
    if not stock_code:
        raise ValueError("stock_code is required")

    try:
        alert_type = AlertType(rule.get("alert_type", ""))
    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

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Use one of the exact values: 'active', 'triggered', 'expired', 'dismissed'.
  2. Omit the status key entirely if you want the default lifecycle state rather than sending a placeholder.
  3. Map external status vocabularies to AlertStatus values at the import boundary.

Example fix

// before
rule = {"stock_code": "AAPL", "alert_type": "volume_spike", "status": "enabled"}
# ValueError: invalid status: enabled

// after
rule = {"stock_code": "AAPL", "alert_type": "volume_spike", "status": "active"}
validate_event_alert_rule(rule)
Defensive patterns

Strategy: validation

Validate before calling

from src.agent.events import AlertStatus

def valid_status(raw) -> bool:
    return raw is None or (isinstance(raw, str) and raw in {s.value for s in AlertStatus})

Type guard

def has_valid_status(rule: dict) -> bool:
    return valid_status(rule.get("status"))

Try / catch

try:
    validate_event_alert_rule(rule)
except ValueError as e:
    if str(e).startswith("invalid status"):
        rule.pop("status", None)  # fall back to default lifecycle
        validate_event_alert_rule(rule)

Prevention

When it happens

Trigger: validate_event_alert_rule with rule['status'] set to something like 'ENABLED', 'on', 'paused', '', or an int. Passing None does NOT trigger it (status is None skips validation), so only truthy garbage values fail.

Common situations: Serialized rules edited by hand or by an LLM that uses a different status vocabulary; importing rules from another system whose status names differ; frontend sending a localized or display-label status string.

Related errors


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