ZhuLinsen/daily_stock_analysis · error · TimeoutError

{call_name} 调用超过 {wait_seconds:g}s,已放弃等待

Error message

{call_name} 调用超过 {wait_seconds:g}s,已放弃等待

What it means

TimeoutError raised by AkshareFetcher's subprocess watchdog: each akshare call runs in a dedicated child process (multiprocessing with a pipe), and if the child does not produce a result within wait_seconds, the parent gives up, terminates the child, and raises this error. It exists because akshare's network calls can hang indefinitely with no internal timeout.

Source

Thrown at data_provider/akshare_fetcher.py:350

    wait_seconds = _AKSHARE_HISTORY_CALL_TIMEOUT if timeout is None else float(timeout)

    multiprocessing.freeze_support()
    ctx = multiprocessing.get_context(_AKSHARE_TIMEOUT_PROCESS_START_METHOD)
    parent_conn, child_conn = ctx.Pipe(duplex=False)
    process = ctx.Process(
        target=_akshare_timeout_worker,
        args=(child_conn, func, args, kwargs),
        name=f"akshare-{call_name}",
        daemon=True,
    )

    process.start()
    child_conn.close()

    try:
        if not parent_conn.poll(wait_seconds):
            _terminate_akshare_process(process)
            raise TimeoutError(f"{call_name} 调用超过 {wait_seconds:g}s,已放弃等待")

        try:
            ok, value = parent_conn.recv()
        except EOFError as exc:
            raise RuntimeError(f"{call_name} 调用进程未返回结果") from exc
    finally:
        parent_conn.close()
        process.join(_AKSHARE_TIMEOUT_PROCESS_JOIN_GRACE)
        _terminate_akshare_process(process)

    if ok:
        return value
    raise value


def _akshare_timeout_worker(conn, func, args, kwargs) -> None:
    try:
        conn.send((True, func(*args, **kwargs)))

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Retry the request — transient upstream slowness is the most common cause; the fetcher design already isolates each call in a fresh process so a retry is safe.
  2. Raise the timeout (wait_seconds / akshare timeout configuration) if the data source is legitimately slow.
  3. Check egress connectivity to akshare's hosts (eastmoney, sina) from the deployment host.
  4. Upgrade/downgrade akshare if an endpoint regression makes calls hang (check akshare changelog).
  5. In multi-source setups, let the provider fallback chain route to the next fetcher (e.g. yfinance) on this TimeoutError.

Example fix

# before
ak.stock_zh_a_hist(symbol='600519', period='daily')  # may hang forever

# after (pattern the fetcher itself uses)
from data_provider.akshare_fetcher import AkshareFetcher
AkshareFetcher().fetch_stock_data('600519', '2024-01-01', '2024-12-31')  # bounded by subprocess watchdog
Defensive patterns

Strategy: retry

Validate before calling

# cheap reachability check before a long fetch
import requests
requests.head('https://push2his.eastmoney.com', timeout=5)  # raises fast if egress is dead

Type guard

def is_akshare_timeout(exc: Exception) -> bool:
    return isinstance(exc, TimeoutError) and '调用超过' in str(exc)

Try / catch

for attempt in range(2):
    try:
        df = fetcher.fetch_stock_data(code, start, end)
        break
    except TimeoutError as e:
        if attempt == 1:
            df = fallback_fetcher.fetch_stock_data(code, start, end)  # e.g. yfinance

Prevention

When it happens

Trigger: Any AkshareFetcher fetch path wrapped by the timeout worker (A-share/ETF/HK history via akshare) where the akshare HTTP call stalls longer than the configured wait_seconds — e.g. slow or unresponsive Eastmoney/Sina endpoints, network black-holing, or DNS hang.

Common situations: Running batch analysis for many stocks during peak hours when the data source is slow; the deployment machine has restricted egress so TCP connections hang instead of failing fast; an akshare version whose underlying endpoint changed and now stalls.

Related errors


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