nautechsystems/nautilus_trader · error

API credentials not configured

Error message

API credentials not configured

What it means

When the AX data config DOES have API credentials configured (`has_api_credentials()` is true), the factory tries to resolve them into a `Credential` via `Credential::resolve`. If resolution returns None — the provided key/secret are empty or cannot be combined into a usable credential — the factory raises "API credentials not configured". It protects against silently building an authenticated client with blank credentials.

Solutions

  1. Set both AX_API_KEY and AX_API_SECRET (or the config's `api_key`/`api_secret` fields) to non-empty values.
  2. If you intended an unauthenticated client, remove the credential fields entirely instead of passing empty strings so `has_api_credentials()` returns false.
  3. Log/inspect the config (without secrets) to confirm both fields are populated before calling create.
  4. Check for secret-management layers (env templating, vault injection) that may substitute empty values at deploy time.

Example fix

// before
let config = AxDataClientConfig { api_key: Some("".into()), api_secret: Some(secret.into()), .. };

// after
let config = AxDataClientConfig {
    api_key: Some(api_key), // non-empty, e.g. from env
    api_secret: Some(secret),
    ..
};
Defensive patterns

Strategy: validation

Validate before calling

// before create
let key = ax_config.api_key.as_ref().map(|k| k.into_inner()).unwrap_or_default();
let secret = ax_config.api_secret.as_ref().map(|s| s.into_inner()).unwrap_or_default();
if !key.is_empty() || !secret.is_empty() {
    assert!(!key.is_empty() && !secret.is_empty(), "AX credentials must be both set or both unset");
}

Try / catch

match factory.create(name, config, ...) {
    Err(e) if e.to_string() == "API credentials not configured" => {
        eprintln!("check AX_API_KEY / AX_API_SECRET: set both, or neither for unauthenticated use");
        Err(e)
    }
    other => other,
}

Prevention

When it happens

Trigger: Calling `AxDataClientFactory.create` with a config where `api_key`/`api_secret` are set to empty strings, or where only one of key/secret is present and `Credential::resolve` cannot form a pair.

Common situations: Environment variables or config file containing empty-string API key/secret; a placeholder value stripped to empty by secret redaction; partial credential setup where only `api_key` was filled in.

Related errors


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

Appendix: source

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

    ) -> 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()),
                None, // orders_base_url
                ax_config.http_timeout_secs,
                ax_config.max_retries,
                ax_config.retry_delay_initial_ms,
                ax_config.retry_delay_max_ms,
                ax_config
                    .proxy_url
                    .as_ref()
                    .map(|url| url.expose_secret().to_owned()),
            )
            .map_err(|e| anyhow::anyhow!("Failed to create HTTP client: {e}"))?
        } else {
            AxHttpClient::new(

View on GitHub (pinned to 18893faf8b)