nautechsystems/nautilus_trader · critical · anyhow::Error

Authentication failed: {e}

Error message

Authentication failed: {e}

What it means

Thrown during the AX execution client's connect flow when Architect AX rejects or fails the login call. AxExecutionClient::authenticate passes the resolved api_key/api_secret to http_client.authenticate(...) to obtain the bearer session token (AX_AUTH_TOKEN_TTL_SECS lifetime); the wrapped {e} is the underlying AxHttpError (HTTP 401/403, network failure, or timeout). Without this token no authenticated trading endpoint can be used, so connect() fails and the node cannot start trading.

Source

Thrown at crates/adapters/architect_ax/src/execution.rs:157

            config,
            emitter,
            http_client,
            ws_orders,
            ws_stream_handle: None,
            auth_refresh_handle: None,
            pending_tasks: TaskHandles::default(),
        })
    }

    async fn authenticate(&self, credential: &Credential) -> anyhow::Result<String> {
        self.http_client
            .authenticate(
                credential.api_key(),
                credential.api_secret(),
                AX_AUTH_TOKEN_TTL_SECS,
            )
            .await
            .map_err(|e| anyhow::anyhow!("Authentication failed: {e}"))
    }

    fn update_account_state(&self) {
        let http_client = self.http_client.clone();
        let account_id = self.core.account_id;
        let emitter = self.emitter.clone();
        let clock = self.clock;

        self.spawn_task("query_account", async move {
            let account_state = http_client
                .request_account_state(account_id)
                .await
                .context("failed to request AX account state")?;
            let ts_event = clock.get_time_ns();
            emitter.emit_account_state(
                account_state.balances.clone(),
                account_state.margins.clone(),
                account_state.is_reported,

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Verify the credential pair resolves correctly: print the masked key from Credential::resolve(config.api_key, config.api_secret) or check AX_API_KEY/AX_API_SECRET in the shell
  2. Confirm the key is active on the Architect dashboard and not IP-restricted or expired
  3. Check that base_url_http matches the environment the key belongs to (default prod vs testnet override)
  4. Test raw connectivity with the same key against the AX whoami endpoint from the same host (curl), ruling out proxies/DNS
  5. Retry connect() once credentials are corrected; the failure is at startup so no orders were sent

Example fix

// before: partial env config, secret never exported
//   export AX_API_KEY=...
//   (AX_API_SECRET missing -> login fails)
// after
//   export AX_API_KEY=...
//   export AX_API_SECRET=...
// or set both explicitly in Rust config
let exec_config = AxExecClientConfig::builder()
    .api_key(Some(key.to_string()))
    .api_secret(Some(secret.to_string()))
    .build()?;
Defensive patterns

Strategy: validation

Validate before calling

// Pre-flight the credential pair before building the node
let cred = Credential::resolve(config.api_key.clone(), config.api_secret.clone())
    .context("AX credentials incomplete")?;
let http = AxHttpClient::with_credentials(
    cred.api_key().to_string(),
    cred.api_secret().to_string(),
    Some(config.http_base_url()), None,
    config.http_timeout_secs, 1, 100, 1_000, config.proxy_url.clone(),
)?;
let whoami = futures::executor::block_on(async { http.inner.get_whoami().await })
    .context("AX login rejected these credentials")?;

Try / catch

// On engine build/connect: catch, log masked key, fail fast
match node.run().await {
    Err(e) if e.to_string().contains("Authentication failed") => {
        log::error!("AX login rejected credentials (key {}...): {e:#}",
            &cred.api_key()[..cred.api_key().len().min(6)]);
        return Err(e); // do NOT retry with the same pair in a loop
    }
    other => other,
}

Prevention

When it happens

Trigger: Engine/node start with a Strategy using the AX execution client whose api_key/api_secret (config fields or AX_API_KEY/AX_API_SECRET env vars) are invalid, expired, or revoked; Architect REST endpoint unreachable (wrong environment/base URL, proxy blocking); clock skew or key IP-restriction causing a 401/403 on login.

Common situations: Rotated or revoked API keys while env vars still hold the old pair; mixing testnet and production credentials; whitespace/newline pasted into the secret; corporate proxy or firewall blocking the login request; typo in the custom base_url_http override.

Understand the failure class

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/04e2ffd7e2bf62d2. Report an issue: GitHub.