{"record":{"id":"4682413d12d93908","repo":"ZhuLinsen/daily_stock_analysis","slug":"stock-code-errors-joined-by-newline","errorCode":null,"errorMessage":"所有数据源获取 {stock_code} 失败:\n{errors joined by newline}","messagePattern":"所有数据源获取 (.+?) 失败:\n(.+?)","errorType":"exception","errorClass":"DataFetchError","httpStatus":null,"severity":"error","filePath":"data_provider/base.py","lineNumber":1494,"sourceCode":"                    fallback_to=fallback_to,\n                )\n                logger.warning(\n                    f\"[数据源失败 {attempt}/{total_fetchers}] [{fetcher.name}] {stock_code}: \"\n                    f\"error_type={error_type}, reason={error_reason}\"\n                )\n                self._record_daily_source_failure(fetcher, market, error_reason)\n                errors.append(error_msg)\n                if attempt < total_fetchers:\n                    next_fetcher = fetchers[attempt]\n                    logger.info(f\"[数据源切换] {stock_code}: [{fetcher.name}] -> [{next_fetcher.name}]\")\n                # 继续尝试下一个数据源\n                continue\n        \n        # 所有数据源都失败\n        error_summary = f\"所有数据源获取 {stock_code} 失败:\\n\" + \"\\n\".join(errors)\n        elapsed = time.time() - request_start\n        logger.error(f\"[数据源终止] {stock_code} 获取失败: elapsed={elapsed:.2f}s\\n{error_summary}\")\n        raise DataFetchError(error_summary)\n    \n    @property\n    def available_fetchers(self) -> List[str]:\n        \"\"\"返回可用数据源名称列表\"\"\"\n        return [f.name for f in self._get_fetchers_snapshot()]\n    \n    def prefetch_realtime_quotes(self, stock_codes: List[str]) -> int:\n        \"\"\"\n        批量预取实时行情数据（在分析开始前调用）\n        \n        策略：\n        1. 检查优先级中是否包含适合预取的数据源（efinance/akshare_em/tushare/tickflow）\n        2. 如果不包含，跳过预取（新浪/腾讯是单股票查询，无需预取）\n        3. 如果自选股数量 >= 5 且使用可预取数据源，则预取填充缓存\n        \n        这样做的好处：\n        - 使用新浪/腾讯时：每只股票独立查询，无全量拉取问题\n        - 使用 efinance/东财/Tushare 时：预取一次，后续缓存命中","sourceCodeStart":1476,"sourceCodeEnd":1512,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/data_provider/base.py#L1476-L1512","documentation":"Terminal failure of the generic (A-share/cn) daily-data failover loop in DataFetcherManager: after the last fetcher, the accumulated per-source error messages are joined and raised as DataFetchError. Equivalent to error 144 but for the non-US/HK routing path.","triggerScenarios":"get_daily_data on an A-share code where every source (efinance, akshare, tushare, ...) fails or is circuit-broken — e.g. Eastmoney anti-bot rate limiting plus missing TUSHARE_TOKEN plus akshare API changes.","commonSituations":"Scheduled daily jobs hammering Eastmoney until all A-share sources are rate-limited; akshare/efinance version drift breaking response parsing across all free sources at once; sources marked unavailable by the failure recorder after repeated runs.","solutions":["Parse the joined per-source messages to identify whether failures are rate limits (slow down / back off) vs parse errors (upgrade akshare/efinance).","Configure a key-based fallback source (e.g. TUSHARE_TOKEN) so the chain has a non-free-tier option.","Reduce request frequency or add caching so consecutive failures don't trip the availability circuit breakers."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"try:\n    df = manager.get_daily_data(code, ...)\nexcept DataFetchError as e:\n    df = cache.get_daily(code) or raise_analysis_skip(code, str(e))","preventionTips":["Rate-limit A-share batch loops to avoid tripping every free source at once.","Provide TUSHARE_TOKEN (or another key source) as a non-Eastmoney fallback.","Run the network-smoke suite before scheduled jobs to detect all-sources-failed early."],"tags":["data-provider","failover","all-sources-failed","a-share","rate-limit"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}