{"record":{"id":"c7d4f28151171c97","repo":"ZhuLinsen/daily_stock_analysis","slug":"date-filtered-summary-matches-too-many-rows-narro","errorCode":null,"errorMessage":"Date-filtered summary matches too many rows; narrow the analysis date range or stock code.","messagePattern":"Date-filtered summary matches too many rows; narrow the analysis date range or stock code\\.","errorType":"validation","errorClass":"ValueError","httpStatus":400,"severity":"warning","filePath":"src/services/backtest_service.py","lineNumber":600,"sourceCode":"                    engine_version=engine_version,\n                    analysis_date_from=analysis_date_from,\n                    analysis_date_to=analysis_date_to,\n                )\n            ew = int(eval_window_days) if eval_window_days is not None else None\n            count = self.repo.count_results(\n                code=code,\n                eval_window_days=ew,\n                engine_version=engine_version,\n                analysis_date_from=analysis_date_from,\n                analysis_date_to=analysis_date_to,\n            )\n            if count > self.MAX_DYNAMIC_SUMMARY_ROWS:\n                if phase_bucket is not None:\n                    raise ValueError(\n                        \"Phase-filtered summary candidate set matches too many rows; \"\n                        \"narrow the analysis date range, stock code, or evaluation window.\"\n                    )\n                raise ValueError(\"Date-filtered summary matches too many rows; narrow the analysis date range or stock code.\")\n            if phase_bucket is not None:\n                rows_with_context = self.repo.list_results_with_context(\n                    code=code,\n                    eval_window_days=ew,\n                    engine_version=engine_version,\n                    analysis_date_from=analysis_date_from,\n                    analysis_date_to=analysis_date_to,\n                    limit=self.MAX_DYNAMIC_SUMMARY_ROWS + 1,\n                )\n                if len(rows_with_context) > self.MAX_DYNAMIC_SUMMARY_ROWS:\n                    raise ValueError(\n                        \"Phase-filtered summary matches too many rows; narrow the analysis date range or stock code.\"\n                    )\n                filtered_pairs = [\n                    (row, snapshot)\n                    for row, snapshot in rows_with_context\n                    if self._phase_bucket_from_snapshot(snapshot) == phase_bucket\n                ]","sourceCodeStart":582,"sourceCodeEnd":618,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/services/backtest_service.py#L582-L618","documentation":"Same guard as the phase-filtered variant, but for a plain date/code-filtered summary request: repo.count_results over the given filters exceeds MAX_DYNAMIC_SUMMARY_ROWS, so the service refuses to build the dynamic summary rather than aggregate an unbounded set. Raised as ValueError → HTTP 400 invalid_params.","triggerScenarios":"GET /api/v1/backtest/summary without analysis_phase, with loose or absent date/code filters, once matched rows exceed MAX_DYNAMIC_SUMMARY_ROWS.","commonSituations":"Long-running deployments accumulating backtest_results; web UI loading the summary tab with default (empty) filters; switching engine versions so the previously-narrowing filter no longer matches.","solutions":["Set analysis_date_from/analysis_date_to to a window whose row count is under the cap.","Scope to one stock with code=.","Reduce stored volume (prune or archive old backtest_results) if unbounded summaries are a product requirement."],"exampleFix":"# before\nsummary = service.get_backtest_summary()\n\n# after\nsummary = service.get_backtest_summary(code=\"600519\", analysis_date_from=\"2026-08-01\")","handlingStrategy":"validation","validationCode":"count = service.repo.count_results(code=code, eval_window_days=eval_window_days, engine_version=engine_version, analysis_date_from=analysis_date_from, analysis_date_to=analysis_date_to)\nif count > service.MAX_DYNAMIC_SUMMARY_ROWS:\n    analysis_date_from, analysis_date_to = shrink_range(analysis_date_from, analysis_date_to)\nsummary = service.get_backtest_summary(code=code, analysis_date_from=analysis_date_from, analysis_date_to=analysis_date_to)","typeGuard":null,"tryCatchPattern":"try:\n    summary = service.get_backtest_summary(code=code, analysis_date_from=dfrom, analysis_date_to=dto)\nexcept ValueError as exc:\n    if \"too many rows\" in str(exc):\n        return auto_narrow_and_retry(code, dfrom, dto)  # retry once with halved range\n    raise","preventionTips":["Always pair unbounded summary calls with at least one narrowing filter.","Cache row counts and proactively warn users when approaching the cap.","Keep scheduled backtests pruned to a retention window."],"tags":["backtest","summary","row-limit","validation"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}