nautechsystems/nautilus_trader · error

Invalid config type for AxDataClientFactory. Expected…

Error message

Invalid config type for AxDataClientFactory. Expected AxDataClientConfig, was {config:?}

What it means

AxDataClientFactory::create downcasts the generic client config to `AxDataClientConfig`; if the passed config is any other type the factory cannot build an AX data client and raises this error including the debug representation of the received config. It is a factory/type-mismatch guard, not a network problem.

Solutions

  1. Construct and pass an `AxDataClientConfig` to the AX data factory.
  2. Check that the config registered for this factory name is the AX data config, not the execution config.
  3. Read the `{config:?}` in the message to see which type was actually passed and fix the wiring.
  4. If configs are built generically, add a match/downcast step that routes each config to its matching factory.

Example fix

// before
let data_config = AxExecutionClientConfig { ... };
factory.create(name, data_config, ...)?; // wrong type

// after
let data_config = AxDataClientConfig {
    api_key: Some(api_key),
    instrument_provider: InstrumentProviderConfig::load(true),
    http_base_url: None,
    ..Default::default()
};
factory.create(name, data_config, ...)?;
Defensive patterns

Strategy: type-guard

Validate before calling

// before calling the factory
let Some(_) = config.as_any().downcast_ref::<AxDataClientConfig>() else {
    panic!("config passed to AxDataClientFactory must be AxDataClientConfig");
};

Type guard

fn as_ax_data_config(config: &dyn Any) -> Option<&AxDataClientConfig> {
    config.downcast_ref::<AxDataClientConfig>()
}

Try / catch

match factory.create(name, config, ...) {
    Ok(client) => client,
    Err(e) if e.to_string().contains("Invalid config type for AxDataClientFactory") => {
        return Err(anyhow::anyhow!("wiring mistake: pass AxDataClientConfig, got: {e}"));
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `AxDataClientFactory.create(name, config, ...)` with a config object that is not an `AxDataClientConfig` — e.g. an `AxExecutionClientConfig`, another venue's data config, or a generic `DataClientConfig`.

Common situations: Registering the AX data factory but passing the execution client's config; wiring configs by position/variable name in a multi-venue setup; copying config-construction code from another adapter.

Related errors


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

Appendix: source

Thrown at crates/adapters/architect_ax/src/factories.rs:94

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

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

        let client_id = ClientId::from(name);

        let http_client = if ax_config.has_api_credentials() {
            let credential = Credential::resolve(
                ax_config.api_key.clone().map(|value| value.into_inner()),
                ax_config.api_secret.clone().map(|value| value.into_inner()),
            )
            .ok_or_else(|| anyhow::anyhow!("API credentials not configured"))?;

            AxHttpClient::with_credentials(
                credential.api_key().to_string(),
                credential.api_secret().to_string(),
                Some(ax_config.http_base_url()),

View on GitHub (pinned to 18893faf8b)