nautechsystems/nautilus_trader · warning · OKXInstrumentDefinitionError

instrument is in pre-open state

Error message

instrument is in pre-open state

What it means

A deliberate lookup failure raised when the requested instrument's OKX state is Preopen. Pre-open instruments have incomplete or empty metadata fields (e.g. unset tick size or lot size), so parsing them would produce an invalid instrument; the library refuses and wraps the reason in OKXInstrumentDefinitionError with the symbol.

Source

Thrown at crates/adapters/okx/src/http/client.rs:2620

            params.inst_id(symbol);

            let params = params.build().map_err(|e| anyhow::anyhow!(e))?;

            self.inner
                .get_instruments(params)
                .await
                .map_err(|e| anyhow::anyhow!(e))?
        };

        let raw_inst = resp
            .first()
            .ok_or_else(|| InstrumentLookupError::not_found(instrument_id))?;

        // Skip pre-open instruments which have incomplete/empty field values
        if raw_inst.state == OKXInstrumentStatus::Preopen {
            return Err(OKXInstrumentDefinitionError::new(
                symbol,
                anyhow::anyhow!("instrument is in pre-open state"),
            )
            .into());
        }

        let fee_rate_opt = {
            let fee_params = GetTradeFeeParams {
                inst_type: instrument_type,
                uly: None,
                inst_family: None,
            };

            match self.inner.get_trade_fee(fee_params).await {
                Ok(rates) => rates.into_iter().next(),
                Err(OKXHttpError::MissingCredentials) => {
                    log::debug!("Missing credentials for fee rates, using None");
                    None
                }
                Err(e) => {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Wait until the instrument's pre-open interval ends and its state becomes Live, then retry the lookup.
  2. Filter out Preopen-state instruments when enumerating instrument lists (the bulk loader already skips them).
  3. Handle OKXInstrumentDefinitionError for this symbol gracefully and continue with other instruments.
  4. Poll periodically until the instrument definition resolves successfully.

Example fix

// before
let instrument = client.request_instrument(symbol)?;
// after
let instrument = match client.request_instrument(symbol) {
    Ok(inst) => inst,
    Err(e) if e.to_string().contains("pre-open") => {
        log::warn!("{symbol} is pre-open; deferring");
        return Ok(None);
    }
    Err(e) => return Err(e.into()),
};
Defensive patterns

Strategy: try-catch

Type guard

fn is_preopen_error(err: &anyhow::Error) -> bool {
    err.to_string().contains("pre-open state")
}

Try / catch

match client.request_instrument(symbol).await {
    Ok(inst) => Ok(Some(inst)),
    Err(e) if e.to_string().contains("pre-open") => {
        log::info!("{symbol} pre-open; retry later");
        Ok(None)
    }
    Err(e) => Err(e.into()),
}

Prevention

When it happens

Trigger: Requesting an instrument definition (request_instrument) whose OKX response has state == Preopen, typically for newly listed contracts (futures/options/swaps) that are listed before trading opens.

Common situations: Subscribing to or loading a brand-new contract on/just after listing day; enumerating all instruments of a type and hitting one in pre-open; stale cached listings referencing not-yet-live contracts.

Understand the failure class

Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/362edf42c0e247b7. Report an issue: GitHub.