ZhuLinsen/daily_stock_analysis · error · ValueError

analysis_phase must be one of premarket, intraday, postmarke

Error message

analysis_phase must be one of premarket, intraday, postmarket, unknown

What it means

_normalize_phase_filter lowercases and trims the analysis_phase argument, treating None/''/'all' as no filter, and only accepts the four buckets premarket, intraday, postmarket, unknown. Anything else raises ValueError → HTTP 400 invalid_params on the results/summary/performance endpoints.

Source

Thrown at src/services/backtest_service.py:799

                market_phase=bucket,
                raw_result=raw_result,
                report_type=report_type,
                analysis_sentiment_score=analysis_sentiment_score,
            )
            for result, stock_name, trend_prediction, summary, bucket, raw_result, report_type, analysis_sentiment_score in page_rows
        ]
        return {"total": matched_total, "page": page, "limit": limit, "items": items}

    @staticmethod
    def _normalize_phase_filter(value: Optional[str]) -> Optional[str]:
        if value is None:
            return None
        text = str(value or "").strip().lower()
        if not text or text == "all":
            return None
        allowed = {"premarket", "intraday", "postmarket", "unknown"}
        if text not in allowed:
            raise ValueError("analysis_phase must be one of premarket, intraday, postmarket, unknown")
        return text

    @staticmethod
    def _phase_bucket_from_summary(summary: Optional[Dict[str, Any]]) -> str:
        if not isinstance(summary, dict):
            return "unknown"
        return normalize_analysis_phase_bucket(summary.get("phase"))

    @classmethod
    def _phase_bucket_from_snapshot(cls, context_snapshot: Optional[str]) -> str:
        return cls._phase_bucket_from_summary(extract_market_phase_summary(context_snapshot))

    @classmethod
    def _phase_counts_from_contexts(cls, snapshots: List[Optional[str]]) -> Dict[str, Dict[str, int]]:
        phase_breakdown = {"premarket": 0, "intraday": 0, "postmarket": 0, "unknown": 0}
        raw_phase_counts: Dict[str, int] = {}
        for snapshot in snapshots:
            summary = extract_market_phase_summary(snapshot)

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Use exactly one of: premarket, intraday, postmarket, unknown (case-insensitive).
  2. Omit the parameter or pass 'all' to disable phase filtering.
  3. Align the web UI select options with this enum (BacktestAnalysisPhaseQuery already enumerates them in the API layer).

Example fix

# before
service.get_recent_evaluations(analysis_phase="after_hours")

# after
service.get_recent_evaluations(analysis_phase="postmarket")
Defensive patterns

Strategy: type-guard

Validate before calling

ALLOWED_PHASES = {"premarket", "intraday", "postmarket", "unknown"}
phase_norm = (analysis_phase or "").strip().lower()
if phase_norm and phase_norm != "all":
    assert phase_norm in ALLOWED_PHASES, f"bad phase: {analysis_phase}"

Type guard

ALLOWED_PHASES = {"premarket", "intraday", "postmarket", "unknown"}

def isValidPhase(value: str | None) -> bool:
    text = (value or "").strip().lower()
    return text in ("", "all") or text in ALLOWED_PHASES

Try / catch

try:
    data = service.get_recent_evaluations(analysis_phase=phase)
except ValueError as exc:
    if "analysis_phase" in str(exc):
        return JSONResponse(status_code=400, content={"error": "invalid_phase", "allowed": sorted(ALLOWED_PHASES)})
    raise

Prevention

When it happens

Trigger: Calling /api/v1/backtest/results?analysis_phase=pre-market, 'PREMARKET ' works but 'premarket/open', 'close', 'afterhours', 'ALL' variant casing is fine — concretely any value outside the set, e.g. analysis_phase=after_hours or analysis_phase=0.

Common situations: Frontend dropdown using different phase labels than the backend enum; users typing free text; passing 'All' works (case-insensitive) but 'any' or '*' does not.

Related errors


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