QuantConnect/Lean · error · AssertionError

{symbols}, {symbol.id}, {symbol}, {ticker}. {e}

Error message

{symbols}, {symbol.id}, {symbol}, {ticker}. {e}

What it means

Catch-all in assert_history_index: the helper performs a series of indexing access styles (df.loc, df.xs, df.at, df2, df3) inside a try block to validate that Lean-created dataframes are indexable by ticker, Symbol, and SID string forms. If any of those access styles raises, the except wraps the original exception with the symbol set, the Symbol id/object, the ticker, and the original error for diagnosis.

Source

Thrown at Algorithm.Python/PandasDataFrameHistoryAlgorithm.py:136

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

            # str : Symbol.VALUE
            if len(ticker) == 0:
                return
            self.assert_history_count(f"df.loc[{ticker}]", df.loc[ticker], expected)
            self.assert_history_count(f"df.xs({ticker})", df.xs(ticker), expected)
            self.assert_history_count(f"df.at[(ticker,), '{column}']", list(df.at[(ticker,), column]), expected)
            self.assert_history_count(f"df2.loc[{ticker}]", df2.loc[ticker], len(df2.columns))
            self.assert_history_count(f"df3[{ticker}]", df3[ticker], expected)
            self.assert_history_count(f"df3.get({ticker})", df3.get(ticker), expected)

        except Exception as e:
            symbols = set(df.index.get_level_values(level='symbol'))
            raise AssertionError(f"{symbols}, {symbol.id}, {symbol}, {ticker}. {e}")


    def assert_history_count(self, method_call, trade_bar_history, expected):
        if isinstance(trade_bar_history, list):
            count = len(trade_bar_history)
        else:
            count = len(trade_bar_history.index)
        if count != expected:
            raise AssertionError(f"{method_call} expected {expected}, but received {count}")


class QuandlFuture(PythonQuandl):
    '''Custom quandl data type for setting customized value column name. Value column is used for the primary trading calculations and charting.'''
    def __init__(self):
        self.value_column_name = "Settle"

View on GitHub (pinned to d2c3659f87)

Solutions

  1. Read the original error ({e}) in the wrapped message — it identifies which access style and which key form failed.
  2. Inspect the dataframe index labels (df.index) to confirm which key form (ticker, str(Symbol.ID), or Symbol) is actually present.
  3. If the PandasMapper cannot map the ticker to a SID, ensure SymbolCache has the symbol registered (e.g. it was added via AddEquity/AddData) before indexing.
  4. For non-unique index errors, deduplicate or use a more specific key (include the time level).

Example fix

# before
except Exception as e:
    symbols = set(df.index.get_level_values(level='symbol'))
    raise AssertionError(f"{symbols}, {symbol.id}, {symbol}, {ticker}. {e}")

# after (also record the index key forms actually present, to localize the failure)
except Exception as e:
    symbols = set(df.index.get_level_values(level='symbol'))
    sample_keys = list(df.index.get_level_values(0)[:5])
    raise AssertionError(
        f"History indexing failed: symbols={symbols}, "
        f"symbol={symbol}, ticker={ticker}, err={e}, sample_keys={sample_keys}")
Defensive patterns

Strategy: try-catch

Validate before calling

# Confirm the key form is present before the multi-style indexing
labels = set(map(str, df.index.get_level_values(0)))
if str(symbol.id) not in labels and ticker not in labels:
    self.debug(f"Neither SID {symbol.id} nor ticker {ticker} in index {sorted(labels)}")
    return

Type guard

def key_resolvable(df, symbol, ticker):
    """True when the index contains the symbol's SID string or ticker form."""
    labels = set(map(str, df.index.get_level_values(0)))
    return str(symbol.id) in labels or ticker in labels

Try / catch

try:
    # the series of df.loc / df.xs / df.at access styles
    ...
except KeyError as e:
    # Indexing key not found — report the index forms available rather than re-raising opaquely
    labels = sorted(set(map(str, df.index.get_level_values(0))))
    self.debug(f"Key not found for {ticker}/{symbol.id}; index has {labels}: {e}")

Prevention

When it happens

Trigger: Any of the df.loc[ticker], df.xs(ticker), df.at[(ticker,), column], df2.loc[ticker], df3[ticker], or df3.get(ticker) calls inside the try raises (e.g. KeyError because the key form is not in the index, ValueError from a non-unique index, or a TypeError from wrong key shape). The wrapper re-raises with full context. This commonly fires when the PandasMapper key remapping (ticker<->SID) cannot resolve a key form.

Common situations: The dataframe index does not contain the expected key form (e.g. str(Symbol.ID) vs the Symbol object vs the ticker), often after a change to how PandasData builds the multi-index or after a pandas version bump that changed indexing semantics. Custom data whose Symbol/ticker does not match the index labels. A duplicate/non-unique index key causing xs to fail.

Related errors


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