nautechsystems/nautilus_trader · error

Invalid config type for BetfairDataClientFactory. Expected B

Error message

Invalid config type for BetfairDataClientFactory. Expected BetfairDataClientConfig, was {config:?}

What it means

BetfairDataClientFactory.create accepts any ClientConfig but only works with BetfairDataClientConfig. It downcasts the passed config; if the downcast fails it throws this error naming the expected and actual config types. Passing a config built for a different venue or client class triggers it.

Source

Thrown at crates/adapters/betfair/src/factories.rs:78

impl Default for BetfairDataClientFactory {
    fn default() -> Self {
        Self::new()
    }
}

impl DataClientFactory for BetfairDataClientFactory {
    fn create(
        &self,
        name: &str,
        config: &dyn ClientConfig,
        _cache: CacheView,
        _clock: Rc<RefCell<dyn Clock>>,
    ) -> anyhow::Result<Box<dyn DataClient>> {
        let betfair_config = config
            .as_any()
            .downcast_ref::<BetfairDataClientConfig>()
            .ok_or_else(|| {
                anyhow::anyhow!(
                    "Invalid config type for BetfairDataClientFactory. Expected BetfairDataClientConfig, was {config:?}",
                )
            })?
            .clone();

        betfair_config.validate()?;

        let credential = betfair_config.credential()?;
        let stream_config = betfair_config.stream_config();
        let nav_filter = betfair_config.navigation_filter();
        let currency = betfair_config.currency()?;
        let min_notional = betfair_config.min_notional()?;

        let http_client = BetfairHttpClient::new(
            credential.clone(),
            None,
            None,
            None,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Construct the config as BetfairDataClientConfig for this factory.
  2. Check the factory registration/lookup so each factory receives its matching config type.
  3. If loading configs dynamically, verify the config's concrete type before calling create.

Example fix

// before
config = BetfairExecutionClientConfig(...)
client = BetfairDataClientFactory().create(..., config, ...)

// after
config = BetfairDataClientConfig(name=..., venue=..., api_key=..., username=..., password=...)
client = BetfairDataClientFactory().create(..., config, ...)
Defensive patterns

Strategy: type-guard

Validate before calling

if not isinstance(config, BetfairDataClientConfig):
    raise TypeError(f"BetfairDataClientFactory requires BetfairDataClientConfig, got {type(config).__name__}")

Type guard

fn is_betfair_data_config(config: &dyn ClientConfig) -> bool {
    config.as_any().downcast_ref::<BetfairDataClientConfig>().is_some()
}

Try / catch

match factory.create(beneficiary, None, config, cache, clock) {
    Ok(client) => client,
    Err(e) if e.to_string().contains("Invalid config type") => {
        panic!("wiring bug: wrong config for BetfairDataClientFactory: {e}")
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling BetfairDataClientFactory.create with a config that is not BetfairDataClientConfig — e.g. a BetfairExecutionClientConfig, another venue's DataClientConfig, or a generic config.

Common situations: Mixing up data vs execution config types when wiring clients in a node/script; copy-pasted factory registration with the wrong config class; dynamic config loading that yields the wrong concrete type.

Related errors


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