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
- 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
- Confirm the key is active on the Architect dashboard and not IP-restricted or expired
- Check that base_url_http matches the environment the key belongs to (default prod vs testnet override)
- Test raw connectivity with the same key against the AX whoami endpoint from the same host (curl), ruling out proxies/DNS
- 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
- Export both AX_API_KEY and AX_API_SECRET together in deployment scripts; fail the deploy if either is empty
- Use Credential::resolve + a whoami pre-flight in smoke tests before market open
- Never mix testnet and production keys in the same environment
- Rotate keys with overlap: add the new pair, verify whoami, then revoke the old one
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
- Authentication and authorization failures — expired tokens, bad credentials, and missing scopes.
Related errors
- Timeout waiting for account {account_id} to be registered af
- failed to request AX whoami
- AX whoami returned no accounts to resolve fees from
- Binance Futures account state request failed: {e}
- A BacktestNode is required for the bars_with_fills chart
AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16).
Data as JSON: /api/errors/04e2ffd7e2bf62d2.
Report an issue: GitHub.