nautechsystems/nautilus_trader · error
`exp_time` is required for {}
Error message
`exp_time` is required for {} What it means
Thrown by `parse_futures_instrument` when the OKX instrument definition has no `exp_time` (expiry timestamp). The expiry is mandatory because the parser computes the instrument's `expiration_ns` from it; without it, no valid Nautilus futures instrument can be constructed. The error names the offending `inst_id`.
Source
Thrown at crates/adapters/okx/src/common/parse.rs:2310
let settlement_currency =
Currency::get_or_create_crypto_with_context(definition.settle_ccy, Some(&context));
let is_inverse = match definition.ct_type {
OKXContractType::Linear => false,
OKXContractType::Inverse => true,
OKXContractType::None => {
anyhow::bail!(
"Invalid contract type '{}' for {}: expected 'linear' or 'inverse'",
definition.ct_type,
definition.inst_id
)
}
};
let listing_time = definition
.list_time
.ok_or_else(|| anyhow::anyhow!("`list_time` is required for {}", definition.inst_id))?;
let expiry_time = definition
.exp_time
.ok_or_else(|| anyhow::anyhow!("`exp_time` is required for {}", definition.inst_id))?;
let activation_ns = parse_millisecond_timestamp(listing_time);
let expiration_ns = parse_millisecond_timestamp(expiry_time);
if definition.tick_sz.is_empty() {
anyhow::bail!("`tick_sz` is empty for {}", definition.inst_id);
}
let price_increment = Price::from_str(&definition.tick_sz).map_err(|e| {
anyhow::anyhow!(
"Failed to parse `tick_sz` '{}' for {}: {e}",
definition.tick_sz,
definition.inst_id
)
})?;
if definition.lot_sz.is_empty() {
anyhow::bail!("`lot_sz` is empty for {}", definition.inst_id);
}View on GitHub (pinned to 18893faf8b)
Solutions
- Inspect the OKX instruments response for the inst_id and confirm `exp_time` is present; skip instruments that lack it.
- Set `exp_time: Some(<ms timestamp>)` when constructing the definition manually.
- For perpetual swaps without expiry, use an adapter/parser path that models them with a far-future or sentinel expiration instead of requiring exp_time.
Example fix
// before
OkxInstrumentDef { inst_id: "BTC-USD-240329", exp_time: None, .. }
// after
OkxInstrumentDef { inst_id: "BTC-USD-240329", exp_time: Some(1711665600000), .. } Defensive patterns
Strategy: validation
Validate before calling
if definition.exp_time.is_none() {
eprintln!("skipping {}: no exp_time", definition.inst_id);
return Ok(None);
} Type guard
fn has_exp_time(def: &OkxInstrumentDef) -> bool {
def.exp_time.is_some()
} Try / catch
match parse_instrument_any(definition) {
Ok(inst) => inst,
Err(e) if e.to_string().contains("`exp_time` is required") => { skip; }
Err(e) => return Err(e),
} Prevention
- Check exp_time presence for non-perpetual futures before parsing.
- Handle perpetuals via a path that does not require exp_time.
- Log skipped instruments for review instead of failing the whole load.
When it happens
Trigger: Calling `parse_instrument_any`/`parse_futures_instrument` with a definition whose `exp_time` is None — OKX omitted the field in the instruments response or the caller built the struct without it.
Common situations: Perpetual swaps or some OKX instrument records where `exp_time` is absent; hand-built definitions in tests/fixtures; API version changes that made `exp_time` optional on the wire.
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
- `settle_ccy` or `quote_ccy` is required for EVENTS instrumen
- Missing contract_expiry for dated future '{product_id}'
- Invalid contract type '{}' for {}: expected 'linear' or 'inv
- Missing sz for algo order {}
- Cannot determine spread fill quantity: fill_sz='{}' and acc_
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/339bab20158d12e3.
Report an issue: GitHub.