virattt/ai-hedge-fund · error · FDClientError

{method} {path} failed: {exc}

Error message

{method} {path} failed: {exc}

What it means

Raised by FDClient._request (hedge_fund/data/client.py:277) when the underlying HTTP request raises a requests.RequestException — connection errors, DNS failures, TLS errors, timeouts. It is wrapped in FDClientError so the backtest pipeline sees one infrastructure-error type; per that class's docstring, a backtest must crash on this rather than treat it as 'no data'.

Source

Thrown at hedge_fund/data/client.py:277

        """HTTP request with retry on 429.

        Fail-loud contract: raises FDClientError on network errors, HTTP
        errors, and exhausted rate-limit retries. Returns None ONLY for
        404 — "this data doesn't exist" is a data fact, not a failure.
        Silently returning empty on real failures poisons backtests
        (missing data reads as "no signal").

        *path* may be an absolute URL (a ``next_page_url`` from a previous
        response), which is requested verbatim.
        """
        url = path if path.startswith("http") else self.BASE_URL + path
        for attempt, delay in enumerate((*self._RETRY_DELAYS, None)):
            try:
                resp = self._session.request(
                    method, url, timeout=self._timeout, **kwargs,
                )
            except requests.RequestException as exc:
                raise FDClientError(
                    f"{method} {path} failed: {exc}", path=path,
                ) from exc

            if resp.status_code == 429 and delay is not None:
                logger.info(
                    "Rate limited (429), retrying in %ds (attempt %d/%d)",
                    delay, attempt + 1, len(self._RETRY_DELAYS),
                )
                time.sleep(delay)
                continue

            if resp.status_code == 404:
                return None

            if resp.status_code >= 400:
                raise FDClientError(
                    f"{method} {path} returned {resp.status_code}: {resp.text[:200]}",
                    status_code=resp.status_code, path=path,

View on GitHub (pinned to eff8a7320f)

Solutions

  1. Check basic connectivity to the provider host (curl or a single minimal get_prices call) — if offline/VPN-blocked, fix the network first.
  2. If the exception mentions timeout, raise the client's timeout (FDClient(timeout=...)) or request a smaller date range per call.
  3. If behind a corporate proxy, configure HTTPS_PROXY/REQUESTS_CA_BUNDLE in the environment so requests can complete the TLS handshake.
  4. In orchestration code, catch FDClientError and retry the whole backtest after a delay — the client itself only retries 429s, not network errors.

Example fix

# before
bars = client.get_prices("SPY", "2020-01-01", "2024-12-31")  # large range, times out -> FDClientError: GET ... failed: ReadTimeout

# after
client = FDClient(timeout=60)
bars = client.get_prices("SPY", "2020-01-01", "2024-12-31")
Defensive patterns

Strategy: retry

Validate before calling

import socket
from urllib.parse import urlparse

def endpoint_reachable(base_url: str, timeout: float = 3.0) -> bool:
    """Cheap pre-flight: can we resolve+connect to the API host?"""
    u = urlparse(base_url)
    host, port = u.hostname, u.port or (443 if u.scheme == "https" else 80)
    try:
        with socket.create_connection((host, port), timeout=timeout):
            return True
    except OSError:
        return False

Type guard

from hedge_fund.data.client import FDClientError

def is_network_failure(e: FDClientError) -> bool:
    """Network-transport failure (no status_code) vs an HTTP error response."""
    return isinstance(e, FDClientError) and e.status_code is None

Try / catch

from hedge_fund.data.client import FDClientError
import time

def get_with_retry(fn, *args, tries=3, delay=10, **kwargs):
    for i in range(tries):
        try:
            return fn(*args, **kwargs)
        except FDClientError as e:
            if e.status_code is not None or i == tries - 1:
                raise  # HTTP-level error or last try: propagate
            time.sleep(delay * (i + 1))

Prevention

When it happens

Trigger: Any FDClient API call (get_prices, get_financial_metrics, get_company_facts, ...) while offline; the data provider host is unreachable (DNS failure, firewall, VPN down); the request exceeds the client's configured timeout; a proxy or TLS interception breaks the connection. Note this branch does NOT retry — network exceptions fail on the first attempt; only HTTP 429s are retried.

Common situations: Running a backtest without network access; corporate proxy/MITM certificate rejecting the API host; too-aggressive timeout on a large historical range request; transient ISP/DNS outage mid-run. Also absolute next_page_url pagination calls failing when the provider domain changed.

Related errors


AI-assisted analysis of virattt/ai-hedge-fund@eff8a7320f (2026-08-15). Data as JSON: /api/errors/2423754ada3def92. Report an issue: GitHub.