nautechsystems/nautilus_trader · error

Failed to convert balance: {e}

Error message

Failed to convert balance: {e}

What it means

parse_balance_allowance divides the raw USDC balance by USDC_SCALE and calls AccountBalance::from_total_and_locked; if the domain layer rejects the conversion (e.g. invalid money value for the currency), the error is wrapped as 'Failed to convert balance: {e}'. This surfaces a domain-constraint violation while turning a venue balance into a Nautilus AccountBalance.

Source

Thrown at crates/adapters/polymarket/src/execution/parse.rs:717

        filled_qty
    }
}

/// pUSD scale factor: the Polymarket API returns balances in micro-pUSD (10^6 units).
const USDC_SCALE: Decimal = Decimal::from_parts(1_000_000, 0, 0, false, 0);

/// Converts a raw micro-pUSD balance from the Polymarket API into an [`AccountBalance`].
///
/// The API returns balances as integer micro-pUSD (e.g. `20000000` = 20 pUSD).
/// This divides by 10^6 and constructs Money via `Money::from_decimal`, matching
/// the pattern used by dYdX, Deribit, OKX, and other adapters.
pub fn parse_balance_allowance(
    balance_raw: Decimal,
    currency: Currency,
) -> anyhow::Result<AccountBalance> {
    let balance_pusd = balance_raw / USDC_SCALE;
    AccountBalance::from_total_and_locked(balance_pusd, Decimal::ZERO, currency)
        .map_err(|e| anyhow::anyhow!("Failed to convert balance: {e}"))
}

/// Result of walking the order book to compute market order parameters.
#[derive(Debug)]
pub struct MarketPriceResult {
    /// The crossing price (worst level reached) for the signed CLOB order.
    pub crossing_price: Decimal,
    /// Expected base quantity (shares) computed by walking levels at actual prices.
    pub expected_base_qty: Decimal,
}

/// Calculates the market-crossing price and expected base quantity by walking the order book.
///
/// Sorts levels deterministically before walking:
/// - BUY (asks): ascending by price, best (lowest) ask first
/// - SELL (bids): descending by price, best (highest) bid first
///
/// This ensures correct results regardless of the CLOB API's response ordering.

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log and inspect the inner error and the raw balance value to see which domain constraint failed.
  2. Sanity-check balance_raw (reject negatives / implausible magnitudes) before calling parse_balance_allowance.
  3. Verify USDC_SCALE matches the API's current balance units (micro/macro USDC).

Example fix

// before
let balance = parse_balance_allowance(balance_raw, currency)?;
// after
anyhow::ensure!(balance_raw >= Decimal::ZERO, "negative balance_raw: {balance_raw}");
let balance = parse_balance_allowance(balance_raw, currency)?;
Defensive patterns

Strategy: try-catch

Validate before calling

if balance_raw < Decimal::ZERO { return Err(anyhow!("negative balance_raw: {balance_raw}")); }

Try / catch

match parse_balance_allowance(balance_raw, currency) {
    Ok(bal) => emit(bal),
    Err(e) => { log::error!("balance conversion failed: {e:#}; raw={balance_raw}"); /* skip this update, keep last good state */ }
}

Prevention

When it happens

Trigger: fetch_and_emit_account_state receives a balance_raw from the Polymarket API that, after division by USDC_SCALE, cannot form a valid AccountBalance — e.g. a negative balance or a value exceeding currency precision/limits.

Common situations: Venue returning an anomalous (negative or huge) balance during API incidents; a USDC_SCALE mismatch making values overflow the expected precision; a changed API response unit.

Understand the failure class

Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.

Related errors


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