{"record":{"id":"04e2ffd7e2bf62d2","repo":"nautechsystems/nautilus_trader","slug":"authentication-failed-e","errorCode":null,"errorMessage":"Authentication failed: {e}","messagePattern":"Authentication failed: (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"critical","filePath":"crates/adapters/architect_ax/src/execution.rs","lineNumber":157,"sourceCode":"            config,\n            emitter,\n            http_client,\n            ws_orders,\n            ws_stream_handle: None,\n            auth_refresh_handle: None,\n            pending_tasks: TaskHandles::default(),\n        })\n    }\n\n    async fn authenticate(&self, credential: &Credential) -> anyhow::Result<String> {\n        self.http_client\n            .authenticate(\n                credential.api_key(),\n                credential.api_secret(),\n                AX_AUTH_TOKEN_TTL_SECS,\n            )\n            .await\n            .map_err(|e| anyhow::anyhow!(\"Authentication failed: {e}\"))\n    }\n\n    fn update_account_state(&self) {\n        let http_client = self.http_client.clone();\n        let account_id = self.core.account_id;\n        let emitter = self.emitter.clone();\n        let clock = self.clock;\n\n        self.spawn_task(\"query_account\", async move {\n            let account_state = http_client\n                .request_account_state(account_id)\n                .await\n                .context(\"failed to request AX account state\")?;\n            let ts_event = clock.get_time_ns();\n            emitter.emit_account_state(\n                account_state.balances.clone(),\n                account_state.margins.clone(),\n                account_state.is_reported,","sourceCodeStart":139,"sourceCodeEnd":175,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/architect_ax/src/execution.rs#L139-L175","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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"],"exampleFix":"// before: partial env config, secret never exported\n//   export AX_API_KEY=...\n//   (AX_API_SECRET missing -> login fails)\n// after\n//   export AX_API_KEY=...\n//   export AX_API_SECRET=...\n// or set both explicitly in Rust config\nlet exec_config = AxExecClientConfig::builder()\n    .api_key(Some(key.to_string()))\n    .api_secret(Some(secret.to_string()))\n    .build()?;","handlingStrategy":"validation","validationCode":"// Pre-flight the credential pair before building the node\nlet cred = Credential::resolve(config.api_key.clone(), config.api_secret.clone())\n    .context(\"AX credentials incomplete\")?;\nlet http = AxHttpClient::with_credentials(\n    cred.api_key().to_string(),\n    cred.api_secret().to_string(),\n    Some(config.http_base_url()), None,\n    config.http_timeout_secs, 1, 100, 1_000, config.proxy_url.clone(),\n)?;\nlet whoami = futures::executor::block_on(async { http.inner.get_whoami().await })\n    .context(\"AX login rejected these credentials\")?;","typeGuard":null,"tryCatchPattern":"// On engine build/connect: catch, log masked key, fail fast\nmatch node.run().await {\n    Err(e) if e.to_string().contains(\"Authentication failed\") => {\n        log::error!(\"AX login rejected credentials (key {}...): {e:#}\",\n            &cred.api_key()[..cred.api_key().len().min(6)]);\n        return Err(e); // do NOT retry with the same pair in a loop\n    }\n    other => other,\n}","preventionTips":["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"],"tags":["authentication","architect-ax","api-credentials","connect","rust"],"backgroundTag":"authentication-failed","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}