{"record":{"id":"8ac6caffbd668b98","repo":"nautechsystems/nautilus_trader","slug":"wallet-balance-snapshot-contains-duplicate-currenc","errorCode":null,"errorMessage":"Wallet balance snapshot contains duplicate currency {}","messagePattern":"Wallet balance snapshot contains duplicate currency (.+?)","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/model/src/defi/wallet.rs","lineNumber":198,"sourceCode":"        self.validate_token_addresses()?;\n\n        let native_currency = self\n            .native_currency\n            .ok_or_else(|| anyhow::anyhow!(\"Wallet balance snapshot has no native currency\"))?;\n        let mut currencies = HashSet::new();\n        currencies.insert(native_currency.currency);\n\n        let mut balances = Vec::with_capacity(self.token_balances.len() + 1);\n        balances.push(AccountBalance::new_checked(\n            native_currency,\n            Money::zero(native_currency.currency),\n            native_currency,\n        )?);\n\n        for token_balance in token_balances {\n            let total = token_balance.as_money()?;\n            if !currencies.insert(total.currency) {\n                anyhow::bail!(\n                    \"Wallet balance snapshot contains duplicate currency {}\",\n                    total.currency\n                );\n            }\n            balances.push(AccountBalance::new_checked(\n                total,\n                Money::zero(total.currency),\n                total,\n            )?);\n        }\n\n        Ok(balances)\n    }\n\n    fn validate_token_addresses(&self) -> anyhow::Result<()> {\n        let mut token_addresses = HashSet::with_capacity(self.token_balances.len());\n        let mut duplicates = Vec::new();\n","sourceCodeStart":180,"sourceCodeEnd":216,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/model/src/defi/wallet.rs#L180-L216","documentation":"Wallet::account_balances builds an AccountBalances from native plus token balances, inserting each currency into a set to guarantee uniqueness. If the snapshot contains the same currency twice (duplicate token entry), building AccountBalances would be invalid, so it bails.","triggerScenarios":"Calling account_balances (directly or via as_account_balances) when token_balances contains two entries with the same Currency — e.g. the same token appearing twice in a wallet snapshot with different balances.","commonSituations":"Data aggregation bugs merging multiple RPC balance responses without deduplication; case-mismatched token addresses producing logically identical currencies; caching layers appending refreshed balances instead of replacing.","solutions":["Deduplicate token_balances by currency before calling account_balances, merging or picking the latest balance","Normalize token addresses/casing when constructing Currency objects so equal tokens map to one Currency","Fix the snapshot producer so each currency appears exactly once"],"exampleFix":"// before\nlet balances = wallet.account_balances(&token_balances)?;\n// after\nlet mut seen = HashMap::new();\nfor tb in token_balances {\n    seen.insert(tb.token.clone(), tb); // keeps last entry\n}\nlet deduped: Vec<_> = seen.into_values().collect();\nlet balances = wallet.account_balances(&deduped)?;","handlingStrategy":"validation","validationCode":"fn has_duplicate_currencies(balances: &[TokenBalance]) -> bool {\n    let mut seen = HashSet::new();\n    balances.iter().any(|b| !seen.insert(b.token.clone()))\n}","typeGuard":null,"tryCatchPattern":"let balances = wallet.account_balances(&token_balances)\n    .map_err(|e| MyError::SnapshotCorrupt(e.to_string()))?;","preventionTips":["Deduplicate token balances by currency before building snapshots","Normalize token addresses so identical tokens produce identical Currency values","Replace (not append) balances on refresh"],"tags":["rust","defi","wallet","duplicates"],"backgroundTag":"invalid-state-transition","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}