ZhuLinsen/daily_stock_analysis · error · DataFetchError
{market_label} {stock_code} 获取失败: {errors joined by newline}
Error message
{market_label} {stock_code} 获取失败:
{errors joined by newline} What it means
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.
Source
Thrown at data_provider/base.py:1408
operation="get_daily_data",
success=False,
latency_ms=duration_ms,
error_type=error_type,
error_message=error_reason,
fallback_to=fallback_to,
)
logger.warning(
f"[数据源失败 {attempt}/{total_fetchers}] [{fetcher.name}] {stock_code}: "
f"error_type={error_type}, reason={error_reason}"
)
self._record_daily_source_failure(fetcher, market, error_reason)
errors.append(error_msg)
break
error_summary = f"{market_label} {stock_code} 获取失败:\n" + "\n".join(errors)
elapsed = time.time() - request_start
logger.error(f"[数据源终止] {stock_code} 获取失败: elapsed={elapsed:.2f}s\n{error_summary}")
raise DataFetchError(error_summary)
for attempt, fetcher in enumerate(fetchers, start=1):
if not self._is_daily_source_available(fetcher, market):
errors.append(self._daily_source_unavailable_error(fetcher))
continue
attempt_start = time.time()
fallback_to = fetchers[attempt].name if attempt < total_fetchers else None
try:
logger.info(f"[数据源尝试 {attempt}/{total_fetchers}] [{fetcher.name}] 获取 {stock_code}...")
record_provider_run_started(
data_type="daily_data",
provider=fetcher.name,
operation="get_daily_data",
)
df = self._call_fetcher_method(
fetcher,
"get_daily_data",
stock_code=stock_code,View on GitHub (pinned to 5159bd72e8)
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.
Defensive patterns
Strategy: fallback
Validate before calling
# cheap preflight: are all US/HK sources circuit-broken?
us_fetchers = manager._filter_fetchers_by_capability(
manager._filter_daily_fetchers_for_market(manager._get_fetchers_snapshot(), 'us'), 'daily_data')
alive = [f for f in us_fetchers if manager._is_daily_source_available(f, 'us')] Try / catch
try:
df = manager.get_daily_data(code, ...)
except DataFetchError as e:
summary = str(e)
if '429' in summary or 'rate' in summary.lower():
schedule_retry(code, backoff=600)
else:
notify(f'{code} daily data unavailable: {summary}') Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- 所有数据源获取 {stock_code} 失败: {errors joined by newline}
- {call_name} 调用超过 {wait_seconds:g}s,已放弃等待
- Akshare 所有渠道获取失败: {last_error}
- Akshare 获取 ETF 数据失败: {e}
- [AlphaVantage] HTTP request failed for {symbol}: {e}
AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15).
Data as JSON: /api/errors/251b864cab977331.
Report an issue: GitHub.