nautechsystems/nautilus_trader · error

Cannot subscribe for externally aggregated synthetic instrum

Error message

Cannot subscribe for externally aggregated synthetic instrument bar data

What it means

NautilusTrader's DataEngine only supports synthetic instruments (instrument IDs like 'SYNTH.AAPL-...') when bars are aggregated internally by the engine's own bar aggregators. Subscribing to a synthetic bar type with AggregationSource::External would require the adapter/data source to feed pre-aggregated bars, which cannot exist for a synthetic instrument that no venue provides. The engine therefore rejects the subscription outright.

Source

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

                "Calling client.execute_subscribe for BookDeltas: {}",
                cmd.instrument_id
            );
            client.execute_subscribe(client_command);
        } else {
            log::error!(
                "Cannot handle command: no client found for client_id={:?}, venue={:?}",
                cmd.client_id,
                cmd.venue,
            );
        }
    }

    fn subscribe_bars(&mut self, cmd: &SubscribeBars) -> anyhow::Result<()> {
        match cmd.bar_type.aggregation_source() {
            AggregationSource::Internal => self.start_bar_aggregation(cmd)?,
            AggregationSource::External => {
                if cmd.bar_type.instrument_id().is_synthetic() {
                    anyhow::bail!(
                        "Cannot subscribe for externally aggregated synthetic instrument bar data"
                    );
                }
            }
        }

        Ok(())
    }

    fn subscribe_synthetic_quotes(&mut self, instrument_id: InstrumentId) {
        let synthetic = match self.cache.borrow().try_synthetic(&instrument_id).cloned() {
            Ok(synthetic) => synthetic,
            Err(e) => {
                log::error!("Cannot subscribe to `QuoteTick` data for synthetic instrument: {e}");
                return;
            }
        };

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Remove the External aggregation source so the BarType uses internal aggregation (default); the engine will aggregate the synthetic bar itself via start_bar_aggregation.
  2. If external aggregation is truly needed, use a real (non-synthetic) instrument ID that the data client can provide bars for.
  3. Build the BarType explicitly with AggregationSource::Internal, e.g. BarType::new(instrument_id, spec, AggregationSource::Internal).

Example fix

// before
let bar_type = BarType::new(synthetic_id, bar_spec, AggregationSource::External);
strategy.subscribe_bars(bar_type)?;
// after
let bar_type = BarType::new(synthetic_id, bar_spec, AggregationSource::Internal);
strategy.subscribe_bars(bar_type)?;
Defensive patterns

Strategy: validation

Validate before calling

if bar_type.instrument_id().is_synthetic() && bar_type.aggregation_source() == AggregationSource::External {
    // rebuild with internal aggregation before subscribing
}
strategy.subscribe_bars(BarType::new(bar_type.instrument_id().clone(), bar_type.spec(), AggregationSource::Internal))?;

Type guard

fn is_subscribable_synthetic_bar(bt: &BarType) -> bool {
    !bt.instrument_id().is_synthetic() || bt.aggregation_source() == AggregationSource::Internal
}

Try / catch

match strategy.subscribe_bars(bar_type) {
    Err(e) if e.to_string().contains("externally aggregated synthetic") => {
        strategy.subscribe_bars(BarType::new(bar_type.instrument_id().clone(), bar_type.spec(), AggregationSource::Internal))?;
    }
    r => r?,
}

Prevention

When it happens

Trigger: Calling subscribe_bars (e.g. via strategy.subscribe_bars) with a BarType whose instrument_id is synthetic while the BarType's aggregation source is External — typically a BarType built with an explicit AggregationSource::External argument.

Common situations: Constructing BarType.from_str with an aggregation source suffix (e.g. ':external') on a synthetic instrument; copying a subscription snippet meant for real instruments and reusing it for synthetic ones; config files that set aggregation_source=external globally.

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