nautechsystems/nautilus_trader · error

Cannot unsubscribe from synthetic instrument `InstrumentClos

Error message

Cannot unsubscribe from synthetic instrument `InstrumentClose` data

What it means

The DataEngine refuses to process an UnsubscribeCommand for InstrumentClose data when the instrument_id is synthetic (e.g. IDs generated by SyntheticInstrument). Synthetic instruments are computed locally and their data streams are internal, so there is no client/provider subscription to tear down.

Source

Thrown at crates/data/src/engine/mod.rs:1279

                if self.is_spread_quote_command(cmd.instrument_id, cmd.params.as_ref()) =>
            {
                self.unsubscribe_spread_quotes(cmd);
                return Ok(());
            }
            UnsubscribeCommand::Trades(cmd) if cmd.instrument_id.is_synthetic() => {
                self.unsubscribe_synthetic_trades(cmd.instrument_id);
                return Ok(());
            }
            UnsubscribeCommand::Instrument(cmd) if cmd.instrument_id.is_synthetic() => {
                anyhow::bail!("Cannot unsubscribe from synthetic instrument `Instrument` data");
            }
            UnsubscribeCommand::InstrumentStatus(cmd) if cmd.instrument_id.is_synthetic() => {
                anyhow::bail!(
                    "Cannot unsubscribe from synthetic instrument `InstrumentStatus` data"
                );
            }
            UnsubscribeCommand::InstrumentClose(cmd) if cmd.instrument_id.is_synthetic() => {
                anyhow::bail!(
                    "Cannot unsubscribe from synthetic instrument `InstrumentClose` data"
                );
            }
            UnsubscribeCommand::OptionGreeks(cmd) if cmd.instrument_id.is_synthetic() => {
                anyhow::bail!("Cannot unsubscribe from synthetic instrument `OptionGreeks` data");
            }
            _ => {}
        }

        if let Some(client) = self.get_command_client(cmd.client_id(), cmd.venue()) {
            client.execute_unsubscribe(cmd);
        } else {
            log::error!(
                "Cannot handle command: no client found for client_id={:?}, venue={:?}",
                cmd.client_id(),
                cmd.venue(),
            );
        }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Do not call unsubscribe for InstrumentClose data on a synthetic instrument ID; synthetic data is managed internally
  2. Verify the instrument_id passed to unsubscribe is a real venue instrument (not one produced by SyntheticInstrument)
  3. Guard the call: check instrument_id.is_synthetic() before issuing the unsubscribe command
  4. If cleanup is needed, cancel the synthetic instrument subscription through the synthetic-instrument API instead

Example fix

// before
engine.unsubscribe(InstrumentClose::new(synthetic_instrument_id, client_id, venue));
// after
if !synthetic_instrument_id.is_synthetic() {
    engine.unsubscribe(InstrumentClose::new(synthetic_instrument_id, client_id, venue));
}
Defensive patterns

Strategy: validation

Validate before calling

if instrument_id.is_synthetic() {
    // skip unsubscribe; synthetic data is engine-managed
    return Ok(());
}

Type guard

fn is_real_instrument(id: &InstrumentId) -> bool {
    !id.is_synthetic()
}

Try / catch

match engine.unsubscribe(cmd) {
    Err(e) if e.to_string().contains("synthetic instrument") => log::warn!("skipped synthetic unsubscribe: {e}"),
    Err(e) => return Err(e),
    Ok(_) => {}
}

Prevention

When it happens

Trigger: Calling DataEngine.unsubscribe with an UnsubscribeCommand::InstrumentClose whose instrument_id.is_synthetic() is true, typically after subscribing to a synthetic instrument's close stream and then unsubscribing by that ID.

Common situations: Configuring a synthetic instrument ID (generated via SyntheticInstrument) where a real venue instrument ID is expected; reusing subscription code between synthetic and real instruments; stale subscription bookkeeping from a prior synthetic subscribe.

Understand the failure class

Background: UnsupportedOperationException and "is not supported" errors: when a library deliberately refuses a call — this error's family across 30 libraries.

Related errors


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