nautechsystems/nautilus_trader · error · anyhow::Error
Empty expiry string
Error message
Empty expiry string
What it means
expiry_timestring_to_unix_nanos converts an IB contract expiry string (e.g. a futures or option last-trade date) into a UnixNanos timestamp. IB expiry strings can legitimately be empty for some contracts, but a parseable expiry is required to build the instrument, so an empty string is rejected immediately.
Source
Thrown at crates/adapters/interactive_brokers/src/providers/parse.rs:73
parts[1].len().min(8) as u8
} else {
0
}
}
/// Convert timestamp string to UnixNanos.
///
/// Handles formats like "20230101" or "20230101 00:00:00 UTC".
///
/// # Errors
///
/// Returns an error if the timestamp cannot be parsed.
pub fn expiry_timestring_to_unix_nanos(
expiry: &str,
details: Option<&ibapi::contracts::ContractDetails>,
) -> anyhow::Result<UnixNanos> {
if expiry.is_empty() {
anyhow::bail!("Empty expiry string");
}
// Parse timestamp string - Most contract expirations are %Y%m%d format
// Some exchanges have expirations in %Y%m%d %H:%M:%S %Z
let dt = if expiry.len() == 8 {
// Format: YYYYMMDD
let year = &expiry[0..4];
let month = &expiry[4..6];
let day = &expiry[6..8];
let date = time::Date::from_calendar_date(
year.parse()?,
time::Month::try_from(month.parse::<u8>()?)?,
day.parse()?,
)?;
// If we have trading hours, try to extract the last trade time
// Trading hours format: "20240411:0000-20240411:1800;..."
let mut expiry_time = time::Time::MIDNIGHT;View on GitHub (pinned to 18893faf8b)
Solutions
- Provide an expiry string (YYYYMMDD, optionally with time) in the contract details before parsing.
- For perpetual/index instruments that genuinely have no expiry, route to the appropriate parser (e.g. index/continuous future) rather than the futures/options parser.
- Check IB contract details are complete — re-request contractDetails if fields came back empty.
- Guard: skip contracts with empty expiry instead of failing the whole batch.
Example fix
// before: empty expiry
let details = ContractDetails { last_trade_date_or_contract_month: "".into(), .. };
// after: expiry supplied
let details = ContractDetails { last_trade_date_or_contract_month: "20260619".into(), .. }; Defensive patterns
Strategy: validation
Validate before calling
fn has_expiry(details: &ContractDetails) -> bool {
!details.last_trade_date_or_contract_month.is_empty()
} Prevention
- Skip or re-fetch contracts with empty expiry fields before parsing
- Use the index/continuous-future parser for expiry-less instruments
- Validate contractDetails completeness after IB responses
When it happens
Trigger: parse_futures_contract or parse_option_contract calls expiry_timestring_to_unix_nanos with contract details whose last_trade_date_or_contract_month (or local expiry field) is empty.
Common situations: Continuous/index-style contracts with no discrete expiry, incomplete contract details returned from IB, or manually built ContractDetails omitting the expiry field.
Understand the failure class
Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.
Related errors
- Invalid expiry format: {}
- Unsupported security type: {:?}
- {e}
- Failed to parse execution timestamp '{time_str}': {e}
- Invalid `external_order_claims` instrument ID {claim}: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/e93ba4cd16825132.
Report an issue: GitHub.