{"record":{"id":"2423754ada3def92","repo":"virattt/ai-hedge-fund","slug":"method-path-failed-exc","errorCode":null,"errorMessage":"{method} {path} failed: {exc}","messagePattern":"(.+?) (.+?) failed: (.+?)","errorType":"exception","errorClass":"FDClientError","httpStatus":null,"severity":"error","filePath":"hedge_fund/data/client.py","lineNumber":277,"sourceCode":"        \"\"\"HTTP request with retry on 429.\n\n        Fail-loud contract: raises FDClientError on network errors, HTTP\n        errors, and exhausted rate-limit retries. Returns None ONLY for\n        404 — \"this data doesn't exist\" is a data fact, not a failure.\n        Silently returning empty on real failures poisons backtests\n        (missing data reads as \"no signal\").\n\n        *path* may be an absolute URL (a ``next_page_url`` from a previous\n        response), which is requested verbatim.\n        \"\"\"\n        url = path if path.startswith(\"http\") else self.BASE_URL + path\n        for attempt, delay in enumerate((*self._RETRY_DELAYS, None)):\n            try:\n                resp = self._session.request(\n                    method, url, timeout=self._timeout, **kwargs,\n                )\n            except requests.RequestException as exc:\n                raise FDClientError(\n                    f\"{method} {path} failed: {exc}\", path=path,\n                ) from exc\n\n            if resp.status_code == 429 and delay is not None:\n                logger.info(\n                    \"Rate limited (429), retrying in %ds (attempt %d/%d)\",\n                    delay, attempt + 1, len(self._RETRY_DELAYS),\n                )\n                time.sleep(delay)\n                continue\n\n            if resp.status_code == 404:\n                return None\n\n            if resp.status_code >= 400:\n                raise FDClientError(\n                    f\"{method} {path} returned {resp.status_code}: {resp.text[:200]}\",\n                    status_code=resp.status_code, path=path,","sourceCodeStart":259,"sourceCodeEnd":295,"githubUrl":"https://github.com/virattt/ai-hedge-fund/blob/eff8a7320fcf0b473b135690fa1a5b0d9b022a83/hedge_fund/data/client.py#L259-L295","documentation":"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'.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Check basic connectivity to the provider host (curl or a single minimal get_prices call) — if offline/VPN-blocked, fix the network first.","If the exception mentions timeout, raise the client's timeout (FDClient(timeout=...)) or request a smaller date range per call.","If behind a corporate proxy, configure HTTPS_PROXY/REQUESTS_CA_BUNDLE in the environment so requests can complete the TLS handshake.","In orchestration code, catch FDClientError and retry the whole backtest after a delay — the client itself only retries 429s, not network errors."],"exampleFix":"# before\nbars = client.get_prices(\"SPY\", \"2020-01-01\", \"2024-12-31\")  # large range, times out -> FDClientError: GET ... failed: ReadTimeout\n\n# after\nclient = FDClient(timeout=60)\nbars = client.get_prices(\"SPY\", \"2020-01-01\", \"2024-12-31\")","handlingStrategy":"retry","validationCode":"import socket\nfrom urllib.parse import urlparse\n\ndef endpoint_reachable(base_url: str, timeout: float = 3.0) -> bool:\n    \"\"\"Cheap pre-flight: can we resolve+connect to the API host?\"\"\"\n    u = urlparse(base_url)\n    host, port = u.hostname, u.port or (443 if u.scheme == \"https\" else 80)\n    try:\n        with socket.create_connection((host, port), timeout=timeout):\n            return True\n    except OSError:\n        return False","typeGuard":"from hedge_fund.data.client import FDClientError\n\ndef is_network_failure(e: FDClientError) -> bool:\n    \"\"\"Network-transport failure (no status_code) vs an HTTP error response.\"\"\"\n    return isinstance(e, FDClientError) and e.status_code is None","tryCatchPattern":"from hedge_fund.data.client import FDClientError\nimport time\n\ndef get_with_retry(fn, *args, tries=3, delay=10, **kwargs):\n    for i in range(tries):\n        try:\n            return fn(*args, **kwargs)\n        except FDClientError as e:\n            if e.status_code is not None or i == tries - 1:\n                raise  # HTTP-level error or last try: propagate\n            time.sleep(delay * (i + 1))","preventionTips":["Wrap the whole backtest in one FDClientError catch that distinguishes status_code None (network) from >=400 (HTTP).","Pre-flight connectivity to the provider host before kicking off hour-long runs.","Set a realistic FDClient timeout for large historical ranges; the default may be tuned to small requests."],"tags":["network","http","data-client","timeout"],"backgroundTag":null,"analyzedSha":"eff8a7320fcf0b473b135690fa1a5b0d9b022a83","analyzedAt":"2026-08-15T00:22:46.567Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}