{"record":{"id":"08c1a0a3d6184aa5","repo":"nautechsystems/nautilus_trader","slug":"no-currency-code-in-account-details","errorCode":null,"errorMessage":"No currency_code in account details","messagePattern":"No currency_code in account details","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/betfair/src/provider.rs","lineNumber":405,"sourceCode":"    pub fn min_notional(&self) -> Option<Money> {\n        self.min_notional\n    }\n\n    /// Fetches the account currency from the Betfair Account API.\n    ///\n    /// # Errors\n    ///\n    /// Returns an error if the API call fails or the currency code is missing/unknown.\n    pub async fn get_account_currency(&self) -> anyhow::Result<Currency> {\n        let details: AccountDetailsResponse = self\n            .http_client\n            .send_accounts(METHOD_GET_ACCOUNT_DETAILS, &serde_json::json!({}))\n            .await\n            .map_err(|e| anyhow::anyhow!(\"{e}\"))?;\n\n        let code = details\n            .currency_code\n            .ok_or_else(|| anyhow::anyhow!(\"No currency_code in account details\"))?;\n        Ok(code.as_str().parse::<Currency>()?)\n    }\n\n    /// Builds an effective filter by merging runtime overrides with the base filter.\n    fn build_effective_filter(\n        &self,\n        overrides: Option<&HashMap<String, String>>,\n    ) -> NavigationFilter {\n        let Some(overrides) = overrides else {\n            return self.nav_filter.clone();\n        };\n\n        let parse_csv = |key: &str| -> Option<Vec<String>> {\n            overrides\n                .get(key)\n                .map(|v| v.split(',').map(|s| s.trim().to_string()).collect())\n        };\n","sourceCodeStart":387,"sourceCodeEnd":423,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/betfair/src/provider.rs#L387-L423","documentation":"getAccountDetails succeeded but the response deserialized with currency_code None. The adapter requires the currency to build Currency values for money/price conversion and refuses to guess, so it errors. A well-formed Betfair response always carries currencyCode, so this points to an unexpected or transformed response body.","triggerScenarios":"The accounts endpoint returning a payload lacking currencyCode — an API change, an intermediary/proxy rewriting the response, or a deserialization mismatch in the adapter's models.","commonSituations":"Version drift between the adapter's response models and the live API; corporate proxies stripping response fields.","solutions":["Set currency explicitly in the config so the lookup is skipped entirely.","Inspect the raw getAccountDetails response to see what actually came back.","Report the payload shape to the adapter maintainers."],"exampleFix":"// before: rely on auto-detection\nlet config = BetfairDataConfig::default();\n\n// after: pin the currency, skip getAccountDetails\nlet config = BetfairDataConfig {\n    currency: Some(Currency::from_str(\"GBP\").unwrap()),\n    ..Default::default()\n};","handlingStrategy":"fallback","validationCode":null,"typeGuard":null,"tryCatchPattern":"let currency = match provider.get_account_currency().await {\n    Ok(c) => c,\n    Err(e) if e.to_string().contains(\"No currency_code\") => {\n        log::warn!(\"currency_code missing from account details; using configured fallback\");\n        config_currency // from BetfairExecConfig/BetfairDataConfig\n    }\n    Err(e) => return Err(e),\n};","preventionTips":["Always configure an explicit currency so the account-details path is never load-bearing.","If auto-detection is required, log the raw response when fields are missing.","Report payload anomalies to adapter maintainers with the response body."],"tags":["betfair","account-api","currency","response-parsing"],"backgroundTag":"missing-response-field","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}