{"record":{"id":"251b864cab977331","repo":"ZhuLinsen/daily_stock_analysis","slug":"market-label-stock-code-errors-joined-b","errorCode":null,"errorMessage":"{market_label} {stock_code} 获取失败:\n{errors joined by newline}","messagePattern":"\\{market_label\\} \\{stock_code\\} 获取失败:\n\\{errors joined by newline\\}","errorType":"exception","errorClass":"DataFetchError","httpStatus":null,"severity":"error","filePath":"data_provider/base.py","lineNumber":1408,"sourceCode":"                            operation=\"get_daily_data\",\n                            success=False,\n                            latency_ms=duration_ms,\n                            error_type=error_type,\n                            error_message=error_reason,\n                            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                    break\n\n            error_summary = f\"{market_label} {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        for attempt, fetcher in enumerate(fetchers, start=1):\n            if not self._is_daily_source_available(fetcher, market):\n                errors.append(self._daily_source_unavailable_error(fetcher))\n                continue\n            attempt_start = time.time()\n            fallback_to = fetchers[attempt].name if attempt < total_fetchers else None\n            try:\n                logger.info(f\"[数据源尝试 {attempt}/{total_fetchers}] [{fetcher.name}] 获取 {stock_code}...\")\n                record_provider_run_started(\n                    data_type=\"daily_data\",\n                    provider=fetcher.name,\n                    operation=\"get_daily_data\",\n                )\n                df = self._call_fetcher_method(\n                    fetcher,\n                    \"get_daily_data\",\n                    stock_code=stock_code,","sourceCodeStart":1390,"sourceCodeEnd":1426,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/data_provider/base.py#L1390-L1426","documentation":"Terminal failure of the market-specific (US/HK/JP/KR/TW) daily-data loop: every fetcher in the routed order either raised or was marked unavailable, and each per-source error message is joined into the summary. The preceding '[数据源失败 i/N]' warnings plus '[数据源终止]' log carry the per-source detail.","triggerScenarios":"get_daily_data on a US/HK/JP/KR/TW code where the whole routed chain fails — e.g. US chain Finnhub->AlphaVantage->Yfinance->Longbridge all erroring (bad keys + rate limits + network outage), or all sources skipped by _is_daily_source_available circuit breakers.","commonSituations":"Expired/absent API keys for Finnhub/AlphaVantage while YFinance is also blocked; sustained rate limiting tripping the per-source failure recorder so sources are treated unavailable; proxy/DNS outage in CI or scheduled Actions runs.","solutions":["Read each line of the joined error summary — it enumerates per-source reasons; fix the most capable source first (e.g. FINNHUB_API_KEY).","Check the _record_daily_source_failure state: sources may be skipped as 'unavailable' from earlier failures; wait out the cooldown or restart the process to reset it.","Verify outbound network access (proxy, DNS, firewall) if all sources report connection-type reasons."],"exampleFix":null,"handlingStrategy":"fallback","validationCode":"# cheap preflight: are all US/HK sources circuit-broken?\nus_fetchers = manager._filter_fetchers_by_capability(\n    manager._filter_daily_fetchers_for_market(manager._get_fetchers_snapshot(), 'us'), 'daily_data')\nalive = [f for f in us_fetchers if manager._is_daily_source_available(f, 'us')]","typeGuard":null,"tryCatchPattern":"try:\n    df = manager.get_daily_data(code, ...)\nexcept DataFetchError as e:\n    summary = str(e)\n    if '429' in summary or 'rate' in summary.lower():\n        schedule_retry(code, backoff=600)\n    else:\n        notify(f'{code} daily data unavailable: {summary}')","preventionTips":["Monitor the per-source '[数据源失败 i/N]' warnings to catch single-source decay before the chain dies.","Configure at least two independent key-based sources per market so one outage isn't fatal.","Cache daily bars so transient chain failures degrade to stale data instead of no data."],"tags":["data-provider","failover","all-sources-failed","network"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}