nautechsystems/nautilus_trader · error · anyhow::Error

invalid {field}='{raw}': {e}

Error message

invalid {field}='{raw}': {e}

What it means

parse_required_decimal is the adapter's generic 'this venue string must be a decimal' helper (it feeds parse_required_quantity_at_precision and parse_required_price_at_precision). It calls Decimal::from_str on the raw string and bails with 'invalid {field}=...' when the string is not a valid decimal literal: rust_decimal rejects empty strings, 'null', stray text, and exponent notation.

Source

Thrown at crates/adapters/binance/src/common/parse.rs:214

}

/// Parses a venue price string into a `Price` at the given precision.
///
/// Returns `None` for unparsable, zero, or negative values. Goes through
/// `Decimal` for exact comparison semantics.
#[must_use]
pub(crate) fn parse_price_at_precision(raw: &str, precision: u8) -> Option<Price> {
    let decimal = Decimal::from_str(raw).ok()?;
    if !decimal.is_sign_positive() || decimal.is_zero() {
        return None;
    }

    Price::from_decimal_dp(decimal, precision).ok()
}

/// Parses a required venue decimal string.
pub(crate) fn parse_required_decimal(raw: &str, field: &str) -> anyhow::Result<Decimal> {
    Decimal::from_str(raw).map_err(|e| anyhow::anyhow!("invalid {field}='{raw}': {e}"))
}

/// Parses a required venue quantity string into a `Quantity` at the given precision.
pub(crate) fn parse_required_quantity_at_precision(
    raw: &str,
    precision: u8,
    field: &str,
) -> anyhow::Result<Quantity> {
    let decimal = parse_required_decimal(raw, field)?;
    Quantity::from_decimal_dp(decimal, precision)
        .map_err(|e| anyhow::anyhow!("invalid {field}='{raw}' at precision {precision}: {e}"))
}

/// Parses a required venue price string into a `Price` at the given precision.
pub(crate) fn parse_required_price_at_precision(
    raw: &str,
    precision: u8,
    field: &str,

View on GitHub (pinned to a4b06ed870)

Solutions

  1. Use the field name and raw value in the error message to identify exactly which endpoint payload is malformed.
  2. Update to the latest adapter version where venue response-format changes are handled.
  3. If the field is legitimately optional on the venue, switch the calling code to the adapter's optional-decimal parsing helpers instead of the required variant.
  4. Re-record or repair test fixtures whose JSON no longer matches the live API.

Example fix

// before: endpoint serializes an empty balance as null
// { "qty": null }

// after: omit the field (optional path) or emit a valid decimal string
// { "qty": "0.00000000" }
Defensive patterns

Strategy: validation

Validate before calling

fn required_decimal_is_valid(raw: &str) -> bool {
    rust_decimal::Decimal::from_str(raw).is_ok()
}

Try / catch

let value = match parse_required_decimal(raw, field) {
    Ok(v) => v,
    Err(e) => {
        tracing::warn!(%field, %raw, "invalid venue decimal: {e}");
        continue; // skip this record
    }
};

Prevention

When it happens

Trigger: Any Binance response path that decodes a required size/price string into a typed value via parse_required_quantity_at_precision / parse_required_price_at_precision (balances, fees, quantities, prices) where the JSON value is null, empty, a boolean, or formatted with an exponent.

Common situations: Binance returns "null" or omits a field for accounts with no activity; a very small value starts rendering in scientific notation; test fixtures were recorded against a different API version; a proxy rewrites the payload.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@a4b06ed870 (2026-08-16). Data as JSON: /api/errors/557c036fba2ad9ed. Report an issue: GitHub.