OpenBB-finance/OpenBB · error · OpenBBError

'underlying_price' was not returned in the provider data.

Error message

'underlying_price' was not returned in the provider data.

 Please set the 'last_price' property and try again.

 Note: This error does not impact the standard OBBject `to_df()` method.

What it means

Raised by the cached OptionsChainsData.dataframe property (options_chains_properties.py). The property builds a DataFrame from the validated model dump; if the provider's data contained no 'underlying_price' column AND the user has not set the manual last_price override, exposure-based columns (DEX/GEX/breakeven) cannot be computed, so the property refuses to build. The plain OBBject.to_df() path is unaffected.

Source

Thrown at openbb_platform/core/openbb_core/provider/utils/options_chains_properties.py:62

    @cached_property
    def dataframe(self) -> "DataFrame":
        """Return all data as a Pandas DataFrame,
        with additional computed columns (Breakeven, GEX, DEX) if available.
        """
        # pylint: disable=import-outside-toplevel
        from numpy import nan
        from pandas import DataFrame, DatetimeIndex, Timedelta, concat, to_datetime

        chains_data = DataFrame(
            self.model_dump(
                exclude_unset=True,
                exclude_none=True,
            )
        )

        if "underlying_price" not in chains_data.columns and not self.last_price:
            raise OpenBBError(
                "'underlying_price' was not returned in the provider data."
                + "\n\n Please set the 'last_price' property and try again."
                + "\n\n Note: This error does not impact the standard OBBject `to_df()` method."
            )

        # Add the underlying price to the DataFrame, or override the existing price.
        if self.last_price:
            chains_data["underlying_price"] = self.last_price

        if chains_data.empty:
            raise OpenBBError("Error: No validated data was found.")

        if "dte" not in chains_data.columns and "eod_date" in chains_data.columns:
            _date = to_datetime(chains_data.eod_date)
            temp = DatetimeIndex(chains_data.expiration)
            temp_ = temp - _date  # type: ignore
            chains_data["dte"] = [Timedelta(_temp_).days for _temp_ in temp_]

View on GitHub (pinned to 3e071fcc2c)

Solutions

  1. Set the manual override before touching .dataframe: result.last_price = 185.50
  2. Or use a provider that returns underlying_price (e.g. tradier/yfinance derivatives endpoints)
  3. If you only need the raw rows, use result.to_df() which bypasses this enriched property

Example fix

# before
res = await obb.derivatives.options.chains(symbol="SPY", provider="...")
df = res.dataframe  # OpenBBError: 'underlying_price' was not returned

# after
res = await obb.derivatives.options.chains(symbol="SPY", provider="...")
res.last_price = 585.20
df = res.dataframe
Defensive patterns

Strategy: validation

Validate before calling

def ensure_underlying_price(res, fallback_price: float):
    if "underlying_price" not in res.to_df().columns and res.last_price is None:
        if fallback_price is None:
            raise ValueError("need a spot price: provider returned none")
        res.last_price = fallback_price
    return res

Type guard

def has_underlying_price(res) -> bool:
    df = res.to_df()
    return "underlying_price" in df.columns or res.last_price is not None

Try / catch

from openbb_core.provider.abstract.data import Data
from openbb_core.app.model.abstract.error import OpenBBError

try:
    df = res.dataframe
except OpenBBError as e:
    if "underlying_price" in str(e):
        res.last_price = get_spot(symbol)  # fetch spot from a quotes endpoint
        df = res.dataframe
    else:
        raise

Prevention

When it happens

Trigger: Accessing result.dataframe (or any chained convenience like total_gex, straddle()) on an options chains result from a provider that does not return underlying_price, without first setting result.last_price = <price>. The check is skipped if last_price was set.

Common situations: Providers whose fetchers return only contract rows without the spot price; historical/delayed snapshots where the spot field is empty; users switching providers and hitting divergent field coverage.

Related errors


AI-assisted analysis of OpenBB-finance/OpenBB@3e071fcc2c (2026-08-14). Data as JSON: /api/errors/0c92a20c75681683. Report an issue: GitHub.