nautechsystems/nautilus_trader · error

Instrument {instrument_id} is expired and no longer availabl

Error message

Instrument {instrument_id} is expired and no longer available for market data requests

What it means

The Polymarket data client rejects historical/request-style market data calls (request_book_snapshot, request_trades) when the target instrument is expired and not reported open. Unlike live subscriptions, this path first resolves the instrument and then validates it is still tradeable/open before issuing the request. It prevents wasted REST calls against closed markets.

Source

Thrown at crates/adapters/polymarket/src/data/mod.rs:371

                "Instrument {instrument_id} is expired and no longer available for live subscription"
            );
        }

        Ok(())
    }

    fn ensure_market_data_request_allowed(
        &self,
        instrument_id: InstrumentId,
    ) -> anyhow::Result<InstrumentAny> {
        let loaded = self.instruments.load();
        let instrument = loaded
            .get(&instrument_id)
            .ok_or_else(|| InstrumentLookupError::not_found(instrument_id))?
            .clone();

        if is_instrument_expired_and_not_reported_open(&instrument, self.clock.get_time_ns()) {
            anyhow::bail!(
                "Instrument {instrument_id} is expired and no longer available for market data requests"
            );
        }

        Ok(instrument)
    }

    fn add_live_subscription_intent(
        &self,
        instrument_id: InstrumentId,
        subscriptions: &Arc<AtomicSet<InstrumentId>>,
    ) -> bool {
        self.add_live_subscription_intent_with_state(instrument_id, subscriptions, || {})
    }

    fn add_delta_subscription_intent(&self, instrument_id: InstrumentId) -> bool {
        self.add_live_subscription_intent_with_state(instrument_id, &self.active_delta_subs, || {
            if self.config.compute_effective_deltas {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Verify the market is open on Polymarket before issuing historical data requests.
  2. Filter the request list to instruments whose end date is in the future.
  3. If historical data for an expired market is needed, use a source that supports closed-market history rather than the live adapter's request path.
  4. Refresh cached instrument metadata if the expiry recorded locally is stale or wrong.

Example fix

// before
let book = client.request_book_snapshot(instrument_id)?;

// after
if instrument.expiration_ts_ns() > clock.get_time_ns() {
    let book = client.request_book_snapshot(instrument_id)?;
} else {
    log::warn!("market {instrument_id} expired; skipping snapshot request");
}
Defensive patterns

Strategy: validation

Validate before calling

if instrument.expiration_ns() <= clock.get_time_ns().as_u64() {
    return Ok(None); // skip request for expired market
}

Try / catch

match client.request_book_snapshot(instrument_id) {
    Ok(book) => Some(book),
    Err(e) if e.to_string().contains("expired") => { log::warn!("expired market {instrument_id}"); None }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling request_book_snapshot or request_trades for an InstrumentId whose cached instrument passes is_instrument_expired_and_not_reported_open at clock.get_time_ns().

Common situations: Backfill jobs or warm-up code requesting snapshots for markets that expired since the config was written; scheduled data pulls that keep running past a market's end date.

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/b77bcc3a712d4402. Report an issue: GitHub.