virattt/ai-hedge-fund · error · FDClientError

{method} {path} returned {resp.status_code}: {resp.text[:200

Error message

{method} {path} returned {resp.status_code}: {resp.text[:200]}

What it means

Raised by FDClient._request (hedge_fund/data/client.py:293) when the provider returns any HTTP >= 400 status other than 429 (rate-limited, retried separately) and 404 (mapped to None = 'no data'). The message embeds the status code and the first 200 chars of the response body. Like all FDClientErrors it is an infrastructure failure: auth problems, bad requests, and server errors all land here.

Source

Thrown at hedge_fund/data/client.py:293

                )
            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,
                )

            return resp

        raise FDClientError(
            f"{method} {path} rate limited (429) after {len(self._RETRY_DELAYS)} retries",
            status_code=429, path=path,
        )

View on GitHub (pinned to eff8a7320f)

Solutions

  1. Read the status_code attribute on FDClientError: 401/403 means fix the API key in .env; 400 means the request params (ticker/date format) are wrong; 5xx means wait and retry.
  2. Verify the API key is present and valid with a one-line probe: FDClient().get_prices('SPY', <recent week>) and inspect the error.
  3. If the body text mentions plan/subscription limits, upgrade the plan or reduce the number of symbols/periods requested.
  4. For 5xx, retry with backoff later — the provider is unhealthy, nothing in your code is wrong.

Example fix

# before
try:
    metrics = client.get_financial_metrics(ticker, as_of, period="ttm", limit=20)
except FDClientError as e:
    raise  # opaque crash

# after
try:
    metrics = client.get_financial_metrics(ticker, as_of, period="ttm", limit=20)
except FDClientError as e:
    if e.status_code in (401, 403):
        raise RuntimeError("API key rejected — check .env") from e
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

def verify_api_key(client) -> bool:
    """One cheap request; 401/403 here beats a crash 40 minutes into a run."""
    try:
        client.get_prices("SPY", "2024-01-02", "2024-01-05")
        return True
    except Exception:
        return False

Type guard

from hedge_fund.data.client import FDClientError

def is_auth_error(e: FDClientError) -> bool:
    return isinstance(e, FDClientError) and e.status_code in (401, 403)

def is_server_error(e: FDClientError) -> bool:
    return isinstance(e, FDClientError) and e.status_code is not None and e.status_code >= 500

Try / catch

from hedge_fund.data.client import FDClientError

try:
    data = client.get_prices(ticker, start, end)
except FDClientError as e:
    if e.status_code in (401, 403):
        raise SystemExit("API key rejected — check the key in .env") from e
    if e.status_code == 400:
        raise ValueError(f"bad request params for {ticker}: {e}") from e
    if e.status_code is not None and e.status_code >= 500:
        logger.warning("provider 5xx, will retry later: %s", e)
        raise
    raise

Prevention

When it happens

Trigger: HTTP 401/403 from an invalid or expired API key; 400 from a malformed query (bad ticker format, invalid date params); 5xx when the provider is down. Concretely: FDClient().get_prices('INVALID%%TICKER', ...) producing a 400, or an unset FMP/API key producing 401 on the first call of a backtest.

Common situations: Expired or forgotten API key in .env; the provider changed its query parameter schema (client library out of date vs API version); free-tier plan limits returning 402/403; scheduled provider maintenance returning 503.

Related errors


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