{"record":{"id":"63aacac3c7b18cd4","repo":"nautechsystems/nautilus_trader","slug":"ax-whoami-account-supplied-no-fee-rates","errorCode":null,"errorMessage":"AX whoami account {} supplied no fee rates","messagePattern":"AX whoami account (.+?) supplied no fee rates","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/architect_ax/src/http/client.rs","lineNumber":1409,"sourceCode":"            .get_whoami()\n            .await\n            .map_err(|e| anyhow::anyhow!(e))\n            .context(\"failed to request AX whoami\")?;\n\n        let Some(account) = whoami.accounts.first() else {\n            anyhow::bail!(\"AX whoami returned no accounts to resolve fees from\");\n        };\n\n        if whoami.accounts.len() > 1 {\n            log::warn!(\n                \"AX credentials cover {} accounts, using fee rates from {}\",\n                whoami.accounts.len(),\n                account.id,\n            );\n        }\n\n        let (Some(maker_fee), Some(taker_fee)) = (account.maker_fee, account.taker_fee) else {\n            anyhow::bail!(\"AX whoami account {} supplied no fee rates\", account.id);\n        };\n\n        let fees = (maker_fee, taker_fee);\n        self.account_fees.store(Some(Arc::new(fees)));\n\n        Ok(fees)\n    }\n\n    /// Requests all instruments from Ax.\n    ///\n    /// Fee rates fall back to the rates last resolved from `GET /whoami`, and to zero when no\n    /// rates have been resolved.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the HTTP request fails or instrument parsing fails.\n    pub async fn request_instruments(\n        &self,","sourceCodeStart":1391,"sourceCodeEnd":1427,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/architect_ax/src/http/client.rs#L1391-L1427","documentation":"Thrown by request_account_fees() when GET /whoami succeeds but the selected account object has maker_fee or taker_fee set to null (the fields are Option<Decimal> in models.rs). The client deliberately refuses to substitute zero because zero is itself a valid fee rate and a silent zero would persist in cached instruments after the cause disappeared. The account id is included so you can identify which AX account is misconfigured.","triggerScenarios":"Calling request_account_fees() (or any startup flow that resolves fees) with credentials whose first whoami account returns \"maker_fee\": null or \"taker_fee\": null. Note an empty string \"\" parses to Decimal::ZERO and does NOT trigger this; only a missing/null field does.","commonSituations":"New or sub-account on AX without a fee schedule attached yet; API key provisioned with entitlements but no trading fee tier; AX backend/API version change dropping the fields from /whoami; test environment accounts that never had fees configured.","solutions":["Verify the account fee schedule with Architect (dashboard or support) and confirm the account id in the error message actually has maker/taker rates assigned.","Check which account is first in whoami.accounts: if your key covers multiple accounts, the client uses the first one, which may be the fee-less account while another account has rates.","Re-run request_account_fees() after the fee schedule is attached; if AX changed the response schema, inspect the raw /whoami JSON and report the contract change.","As a workaround for instrument building, call request_instruments(Some(maker), Some(taker)) with explicit rates - it does not require request_account_fees to succeed."],"exampleFix":"// before\nlet (maker, taker) = client.request_account_fees().await?; // bails on null rates\n\n// after: fall back to explicit rates while the account is fixed\nlet (maker, taker) = match client.request_account_fees().await {\n    Ok(fees) => fees,\n    Err(e) if e.to_string().contains(\"supplied no fee rates\") => {\n        log::warn!(\"AX fee resolution failed ({e}); using configured rates\");\n        (dec!(0.0002), dec!(0.0025))\n    }\n    Err(e) => return Err(e),\n};","handlingStrategy":"try-catch","validationCode":"// No public pre-check exists for whoami contents; validate after the call\nlet fees = client.request_account_fees().await;","typeGuard":null,"tryCatchPattern":"match client.request_account_fees().await {\n    Ok(fees) => { /* proceed */ }\n    Err(e) if e.to_string().contains(\"supplied no fee rates\") => {\n        // account misconfigured: use explicitly configured rates downstream\n        log::warn!(\"AX fee resolution failed: {e}\");\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Provision and verify fee schedules on every AX account before generating API keys for it.","When credentials cover multiple accounts, confirm the first whoami account is the intended trading account - that is the one whose fees are used.","Pass explicit maker_fee/taker_fee to request_instruments so instrument building never depends on whoami succeeding.","Treat this error as configuration, not transient: do not blind-retry; fix the account or credentials."],"tags":["rust","nautilus","architect-ax","fees","whoami","account-config","live-trading"],"backgroundTag":"api-response-missing-field","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}