QuantConnect/Lean · error · AssertionError

Empty history data frame for {symbol}

Error message

Empty history data frame for {symbol}

What it means

Guard at the top of assert_history_index that the passed history dataframe is non-empty before indexing into it. The helper does df.iat[0,0], df.xs(...), and df.unstack(...) which all require at least one row. The assertion fires when a History call returned an empty frame for a symbol, so subsequent indexing would fail with a less clear error.

Source

Thrown at Algorithm.Python/PandasDataFrameHistoryAlgorithm.py:97

        # we can loop over the return value from these functions and we get TradeBars
        # we can use these TradeBars to initialize indicators or perform other math
        self.spy_daily_sma.reset()
        for index, trade_bar in trade_bar_history.loc["SPY"].iterrows():
            self.spy_daily_sma.update(index, trade_bar["close"])

        # we can loop over the return values from these functions and we'll get Quandl data
        # this can be used in much the same way as the trade_bar_history above
        self.spy_daily_sma.reset()
        for index, quandl in quandl_history.loc["CHRIS/CME_SP1"].iterrows():
            self.spy_daily_sma.update(index, quandl["settle"])

        self.set_holdings(self.eur, 1)

    def assert_history_index(self, df, column, expected, ticker, symbol):

        if df.empty:
            raise AssertionError(f"Empty history data frame for {symbol}")
        if column not in df:
            raise AssertionError(f"Could not unstack df. Columns: {', '.join(df.columns)} | {column}")

        value = df.iat[0,0]
        df2 = df.xs(df.index.get_level_values('time')[0], level='time')
        df3 = df[column].unstack(level=0)

        try:

            # str(Symbol.ID)
            self.assert_history_count(f"df.iloc[0]", df.iloc[0], len(df.columns))
            self.assert_history_count(f"df.loc[str({symbol.id})]", df.loc[str(symbol.id)], expected)
            self.assert_history_count(f"df.xs(str({symbol.id}))", df.xs(str(symbol.id)), expected)
            self.assert_history_count(f"df.at[(str({symbol.id}),), '{column}']", list(df.at[(str(symbol.id),), column]), expected)
            self.assert_history_count(f"df2.loc[str({symbol.id})]", df2.loc[str(symbol.id)], len(df2.columns))
            self.assert_history_count(f"df3[str({symbol.id})]", df3[str(symbol.id)], expected)
            self.assert_history_count(f"df3.get(str({symbol.id}))", df3.get(str(symbol.id)), expected)

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Before calling assert_history_index, verify the History result is non-empty for that symbol and, if empty, skip or fail with a clearer upstream message.
  2. Confirm the symbol's data files exist and are non-empty for the requested range.
  3. Ensure the correct data type/resolution is requested for the symbol (e.g. custom data vs equity bars).
  4. Widen the date window or pick a known-populated range for that data source.

Example fix

# before
def assert_history_index(self, df, column, expected, ticker, symbol):
    if df.empty:
        raise AssertionError(f"Empty history data frame for {symbol}")

# after (report the request context that produced the empty frame)
def assert_history_index(self, df, column, expected, ticker, symbol):
    if df.empty:
        raise AssertionError(
            f"Empty history data frame for {symbol} ({ticker}); "
            f"requested range may lack data")
Defensive patterns

Strategy: validation

Validate before calling

# Guard the helper against empty frames before indexing
def assert_history_index(self, df, column, expected, ticker, symbol):
    if df is None or df.empty:
        self.debug(f"Empty history for {symbol}; skipping index assertion")
        return

Type guard

def history_frame_usable(df):
    """True when the frame is non-empty and safe to index."""
    return df is not None and not df.empty and len(df.columns) > 0

Prevention

When it happens

Trigger: assert_history_index is called with a dataframe where df.empty is True. This means the upstream History(...) request for that symbol/type returned no rows — e.g. no data on disk for the symbol/date range, the data type (Quandl/custom) had no rows, or the symbol's history was filtered out by exchange hours/normalization.

Common situations: A custom/Quandl data source produced no rows for the requested window. The symbol's data files are missing or empty for the backtest range. The history request used a resolution/data type the symbol does not support, yielding an empty frame. A date range falls entirely outside the symbol's available data.

Related errors


AI-assisted analysis of QuantConnect/Lean@d2c3659f87 (2026-08-13). Data as JSON: /api/errors/b0d2bf2be2fcdc84. Report an issue: GitHub.