nautechsystems/nautilus_trader · error · anyhow::Error
Cannot cache futures spread: missing put underlying for {put
Error message
Cannot cache futures spread: missing put underlying for {put_instrument_id} What it means
Mirror of the call-side check: cache_futures_spread reads the put option's underlying symbol, and if underlying() is None for the put instrument it bails with this error naming the put instrument. Without the put's underlying the method cannot build the underlying futures ID used as the spread cache key.
Source
Thrown at crates/common/src/greeks.rs:1093
"Cannot cache futures spread: non-option instruments provided call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}"
);
}
if call_instrument.option_kind() != Some(OptionKind::Call)
|| put_instrument.option_kind() != Some(OptionKind::Put)
{
anyhow::bail!(
"Cannot cache futures spread: expected call/put pair call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}"
);
}
let Some(call_underlying) = call_instrument.underlying() else {
anyhow::bail!(
"Cannot cache futures spread: missing call underlying for {call_instrument_id}"
);
};
let Some(put_underlying) = put_instrument.underlying() else {
anyhow::bail!(
"Cannot cache futures spread: missing put underlying for {put_instrument_id}"
);
};
if call_underlying != put_underlying {
anyhow::bail!(
"Cannot cache futures spread: option underlyings differ call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}"
);
}
if call_instrument.strike_price() != put_instrument.strike_price() {
anyhow::bail!(
"Cannot cache futures spread: strike prices differ call_instrument_id={call_instrument_id} put_instrument_id={put_instrument_id}"
);
}
if call_instrument.expiration_ns() != put_instrument.expiration_ns() {
anyhow::bail!(View on GitHub (pinned to 18893faf8b)
Solutions
- Set the underlying field on the put instrument definition before adding it to the Cache.
- Fix the adapter/loader so underlying is populated for all option instruments.
- Pre-check put.underlying().is_some() at the call site and skip with a warning if absent.
Example fix
// before Instrument::new(... /* put built without underlying */ ...) // after Instrument::new(..., Some(underlying_symbol), ...) // put leg must carry the same underlying as the call
Defensive patterns
Strategy: validation
Validate before calling
// rust
fn put_has_underlying(cache: &Cache, put_id: &InstrumentId) -> bool {
cache.instrument(put_id).and_then(|i| i.underlying()).is_some()
}
if !put_has_underlying(&cache, &put_id) {
tracing::warn!("put {put_id} has no underlying; skipping spread");
return Ok(None);
} Type guard
fn put_underlying(cache: &Cache, id: &InstrumentId) -> Option<Ustr> {
cache.instrument(id).filter(|i| i.option_kind() == Some(OptionKind::Put)).and_then(|i| i.underlying())
} Try / catch
let res = greeks.cache_futures_spread(call_id, put_id, future_id);
if let Err(e) = res {
if e.to_string().contains("missing put underlying") {
reload_instrument_definition(put_id);
}
} Prevention
- Apply the same definition validation to every option leg, not just the first.
- Check fixture builders in tests so put instruments are constructed identically to calls.
- Log and quarantine malformed instrument definitions at ingestion.
When it happens
Trigger: Calling cache_futures_spread with a put instrument whose definition lacks an underlying — hand-built fixtures or adapters that leave underlying unset for the put leg.
Common situations: Test fixtures that populate the call fully but build the put minimally; adapters that only set underlying on one leg; serialized instrument definitions with the field dropped.
Understand the failure class
Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.
Related errors
- Cannot cache futures spread: missing call underlying for {ca
- Derive option-chain reference prices require an option instr
- option_summary_family_subs mutex poisoned
- InstrumentLookupError::not_found(instrument_id)
- Invalid `OptionKind`, was '{invalid}'
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/1da95f345b214e03.
Report an issue: GitHub.