nautechsystems/nautilus_trader · warning · anyhow::Error

Account summary currency was empty

Error message

Account summary currency was empty

What it means

parse_currency is a helper that converts an IB account summary currency string into a Nautilus Currency, and it uses anyhow::ensure! to reject empty strings with this message. IB sometimes emits account summary rows with a blank currency, and this guard prevents constructing a meaningless Currency. Callers (merge_account_summary_margin, merge_account_summary_balance, parse_account_summary_to_balance) log a warning and skip the row rather than propagate the error.

Source

Thrown at crates/adapters/interactive_brokers/src/execution/account.rs:563

            // Available funds - this is the free amount
            AccountBalance::from_total_and_free(balance, balance, currency).map_err(Into::into)
        }
        _ => {
            // Default: treat as total balance
            AccountBalance::from_total_and_locked(balance, Decimal::ZERO, currency)
                .map_err(Into::into)
        }
    }
}

fn parse_balance_decimal(value: &str) -> anyhow::Result<Decimal> {
    value
        .parse::<Decimal>()
        .context(format!("Failed to parse balance value: {}", value))
}

fn parse_currency(currency: &str) -> anyhow::Result<Currency> {
    anyhow::ensure!(!currency.is_empty(), "Account summary currency was empty");
    Ok(Currency::from(currency))
}

#[cfg(test)]
mod tests {
    use ibapi::accounts::AccountSummary;
    use nautilus_model::types::{AccountBalance, Currency, MarginBalance, Money};
    use rstest::rstest;
    use rust_decimal::Decimal;

    use super::{
        AccountSummaryTags, check_external_position_change, create_position_tracker,
        merge_account_summary_balance, merge_account_summary_margin, parse_currency,
    };

    fn margin_summary(tag: &str, value: &str, currency: &str) -> AccountSummary {
        AccountSummary {
            account: "DU123".to_string(),

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Filter AccountSummary rows to currency-denominated tags and skip rows with empty currency before calling the merge/parse helpers.
  2. Check the raw IB summary data for rows with blank currency and confirm they are expected placeholders.
  3. If currency-denominated rows have empty currency, verify the IB account subscription parameters (account_id, report type) are correct.

Example fix

// before
let currency = parse_currency(&summary.currency)?;
// after
if summary.currency.is_empty() {
    tracing::debug!("Skipping account summary row with empty currency: {}", summary.tag);
    return Ok(());
}
let currency = parse_currency(&summary.currency)?;
Defensive patterns

Strategy: validation

Validate before calling

fn has_valid_currency(summary: &AccountSummary) -> bool {
    !summary.currency.is_empty()
}

Try / catch

match parse_currency(&summary.currency) {
    Ok(c) => proceed(c),
    Err(e) => tracing::debug!("Skipping row: {}", e),
}

Prevention

When it happens

Trigger: Any account summary update where AccountSummary.currency is an empty string — parse_account_summary_to_balance, merge_account_summary_balance, or merge_account_summary_margin encounter such a row while processing subscribe_account_summary results.

Common situations: IB sends summary rows for tags that are not currency-denominated (e.g. AccountType, AvailableFunds variants, or cash balance rows with blank currency) during initial subscription or after account changes.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/66412fa435fa2e12. Report an issue: GitHub.