{"record":{"id":"0223dce99d3c57f7","repo":"nautechsystems/nautilus_trader","slug":"account-report-contains-multiple-currencies","errorCode":null,"errorMessage":"account report contains multiple currencies","messagePattern":"account report contains multiple currencies","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/nautilus_trader/analysis/tearsheet.py","lineNumber":901,"sourceCode":"                currency_report[\"total\"].iloc[-1],\n            )\n\n    return account_info\n\n\ndef _extract_account_balance_series(\n    report: pd.DataFrame,\n    target_currency: str | None,\n) -> tuple[pd.Series | None, str | None]:\n    if report.empty or \"currency\" not in report or \"total\" not in report:\n        return None, None\n\n    observed_currency: str | None = None\n\n    if target_currency is None:\n        account_currencies = set(report[\"currency\"].dropna())\n        if len(account_currencies) != 1:\n            raise ValueError(\"account report contains multiple currencies\")\n        observed_currency = next(iter(account_currencies))\n    else:\n        report = report[report[\"currency\"] == target_currency]\n\n    if report.empty:\n        return None, observed_currency\n\n    totals = pd.to_numeric(report[\"total\"], errors=\"coerce\")\n    totals.index = report.index\n    totals = totals.dropna().sort_index()\n    if totals.empty:\n        return None, observed_currency\n\n    return totals.groupby(level=0).last(), observed_currency\n\n\ndef _calculate_daily_balance_returns(total_balance: pd.Series) -> pd.Series | None:\n    account_returns = (","sourceCodeStart":883,"sourceCodeEnd":919,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/python/nautilus_trader/analysis/tearsheet.py#L883-L919","documentation":"With subscribe_race_data enabled, the data client queries the session token a second time (after the main stream is up) to connect the race stream at BETFAIR_RACE_STREAM_HOST, and session_token() returned None. Like error 110, the token slot is filled by login and cleared by disconnect/reconnect, so this indicates the session was cleared mid-connect by a concurrent operation, or the session expired and was invalidated between the two reads.","triggerScenarios":"subscribe_race_data: true in the data config and a concurrent disconnect()/reconnect() on the shared HTTP client between main-stream setup and race-stream setup; stopping the node while race stream setup is in progress.","commonSituations":"Shutdown during startup; supervision timeouts that disconnect mid-connect; racing owners of the shared HTTP client.","solutions":["Serialize connect/disconnect: one owner task for the HTTP client lifecycle","Retry connect() to re-login and rebuild both streams","Defer stop/shutdown signals until connect reports completion"],"exampleFix":"// before: read once, fail hard\nlet race_session = self.http_client.session_token().await\n    .ok_or_else(|| anyhow::anyhow!(\"No session token for race stream\"))?;\n\n// after: re-login if the slot was cleared\nlet race_session = match self.http_client.session_token().await {\n    Some(t) => t,\n    None => {\n        self.http_client.reconnect().await?;\n        self.http_client.session_token().await\n            .ok_or_else(|| anyhow::anyhow!(\"No session token for race stream\"))?\n    }\n};","handlingStrategy":"validation","validationCode":"if config.subscribe_race_data {\n    anyhow::ensure!(\n        http_client.is_connected().await,\n        \"session lost before race stream setup; avoid concurrent disconnect during connect\"\n    );\n}","typeGuard":"async fn has_live_session(client: &BetfairHttpClient) -> bool {\n    client.is_connected().await\n}","tryCatchPattern":"match http_client.session_token().await {\n    Some(t) => { /* open race stream */ }\n    None => {\n        // token cleared mid-connect: re-login and rebuild streams once\n        http_client.reconnect().await?;\n    }\n}","preventionTips":["Serialize the whole multi-stream connect sequence under one owner","Delay shutdown signals until connect completes","Log session lifecycle events to spot racing disconnects"],"tags":["betfair","session","race-stream","race-condition","rust"],"backgroundTag":"session-token-missing","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}