ZhuLinsen/daily_stock_analysis · error · ValueError
validation_error
validation_error
Error message
unsupported portfolio alert_type: {alert_type} What it means
ValueError(f'unsupported portfolio alert_type: {alert_type}') with code validation_error is raised by normalize_portfolio_alert_parameters (src/services/portfolio_alerts.py:89) when alert_type is not in PORTFOLIO_ALERT_TYPES = {'portfolio_stop_loss','portfolio_concentration','portfolio_drawdown','portfolio_price_stale'}. It is the gatekeeping check for P6 portfolio alert parameter normalization.
Source
Thrown at src/services/portfolio_alerts.py:84
@dataclass
class StaticAlertEvaluation:
"""Runtime placeholder for skipped/degraded expansion results."""
stock_code: str
alert_type: str
message: str
record_status: str = "skipped"
metadata: Dict[str, Any] = field(default_factory=dict)
description: str = ""
def normalize_portfolio_alert_parameters(alert_type: str, parameters: Dict[str, Any]) -> Dict[str, Any]:
"""Normalize P6 portfolio alert parameters."""
if alert_type not in PORTFOLIO_ALERT_TYPES:
raise ValueError(f"unsupported portfolio alert_type: {alert_type}")
if not isinstance(parameters, dict):
raise ValueError("parameters must be an object")
if alert_type == "portfolio_stop_loss":
mode = str(parameters.get("mode") or "near").strip().lower()
if mode not in {"near", "breach"}:
raise ValueError("portfolio_stop_loss mode must be near or breach")
return {"mode": mode}
return {}
def portfolio_effective_target(target: str) -> str:
target_text = str(target or "all").strip() or "all"
return "account:all" if target_text == "all" else f"account:{target_text}"
def normalize_batch_target_scope_target(target_scope: str, target: str) -> str:View on GitHub (pinned to 5159bd72e8)
Solutions
- Use one of: portfolio_stop_loss, portfolio_concentration, portfolio_drawdown, portfolio_price_stale
- Check the PORTFOLIO_ALERT_TYPES frozenset at src/services/portfolio_alerts.py:20 and align client enums
- If a market-level alert was intended, use the market alert API with MARKET_ALERT_TYPES instead
Example fix
# before
normalize_portfolio_alert_parameters("stop_loss", {"mode": "near"})
# after
normalize_portfolio_alert_parameters("portfolio_stop_loss", {"mode": "near"}) Defensive patterns
Strategy: type-guard
Validate before calling
from src.services.portfolio_alerts import PORTFOLIO_ALERT_TYPES
if alert_type not in PORTFOLIO_ALERT_TYPES:
raise ValueError(f"choose from {sorted(PORTFOLIO_ALERT_TYPES)}") Type guard
from src.services.portfolio_alerts import PORTFOLIO_ALERT_TYPES
def is_valid_portfolio_alert_type(alert_type: str) -> bool:
return isinstance(alert_type, str) and alert_type in PORTFOLIO_ALERT_TYPES Try / catch
try:
params = normalize_portfolio_alert_parameters(alert_type, parameters)
except ValueError as exc:
if "unsupported portfolio alert_type" in str(exc):
return api_error(400, str(exc)) Prevention
- Always use the portfolio_ prefixed type names
- Import PORTFOLIO_ALERT_TYPES instead of duplicating the list
- Keep market and portfolio alert type enums separate in client code
When it happens
Trigger: Creating/updating a portfolio alert with any other type string — e.g. 'stop_loss' (missing portfolio_ prefix), 'portfolio_pnl', or a market alert type like 'market_light_status' passed to the portfolio API.
Common situations: Renamed alert types across versions, clients abbreviating the prefixed names, or confusion between the market-alert and portfolio-alert type namespaces.
Related errors
- parameters must be an object
- validation_error
- Backend executable not found: ${backendPath}
- Invalid share image record ID
- Desktop backend URL is unavailable
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/02131244f807e4d3.
Report an issue: GitHub.