nautechsystems/nautilus_trader · error · anyhow::Error

Failed to parse {field}='{value}' as Decimal: {e}

Error message

Failed to parse {field}='{value}' as Decimal: {e}

What it means

parse_decimal converts an exchange string into a rust_decimal Decimal. This error wraps the conversion failure with the field name and raw value, and is used widely: instrument parsing, funding rates, and the precision-based price/quantity parsers all route raw strings through it.

Source

Thrown at crates/adapters/bybit/src/common/parse.rs:1322

    field: &str,
) -> anyhow::Result<Quantity> {
    let parsed = parse_decimal(value, field)?;
    Quantity::from_decimal_dp(parsed, precision).with_context(|| {
        format!("Failed to construct Quantity for {field} with precision {precision}")
    })
}

pub(crate) fn parse_price(value: &str, field: &str) -> anyhow::Result<Price> {
    Price::from_str(value).map_err(|e| anyhow::anyhow!("Failed to parse {field}='{value}': {e}"))
}

pub(crate) fn parse_quantity(value: &str, field: &str) -> anyhow::Result<Quantity> {
    Quantity::from_str(value).map_err(|e| anyhow::anyhow!("Failed to parse {field}='{value}': {e}"))
}

pub(crate) fn parse_decimal(value: &str, field: &str) -> anyhow::Result<Decimal> {
    Decimal::from_str(value)
        .map_err(|e| anyhow::anyhow!("Failed to parse {field}='{value}' as Decimal: {e}"))
}

pub(crate) fn parse_millis_timestamp(value: &str, field: &str) -> anyhow::Result<UnixNanos> {
    let millis: u64 = value
        .parse()
        .with_context(|| format!("Failed to parse {field}='{value}' as u64 millis"))?;
    let nanos = millis
        .checked_mul(NANOSECONDS_IN_MILLISECOND)
        .context("millisecond timestamp overflowed when converting to nanoseconds")?;
    Ok(UnixNanos::from(nanos))
}

fn resolve_settlement_currency(
    settle_coin: &str,
    base_currency: Currency,
    quote_currency: Currency,
) -> anyhow::Result<Currency> {
    if settle_coin.eq_ignore_ascii_case(base_currency.code.as_str()) {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Inspect the raw value for the named field; confirm it is a finite decimal string.
  2. Handle empty strings explicitly (default/skip) before calling parse_decimal.
  3. Trim and normalize (no commas, no 'NaN'/'inf').
  4. Update the adapter if the Bybit response schema shifted fields.

Example fix

// before
let funding = parse_decimal(&resp.current_funding_rate, "current_funding_rate")?;
// after
let raw = resp.current_funding_rate.trim();
let funding = if raw.is_empty() { Decimal::ZERO } else { parse_decimal(raw, "current_funding_rate")? };
Defensive patterns

Strategy: validation

Validate before calling

// Rust: safe decimal parse helper used before parse_decimal
fn parse_decimal_safe(s: &str) -> Option<rust_decimal::Decimal> {
    let t = s.trim();
    if t.is_empty() || t.eq_ignore_ascii_case("nan") || t.eq_ignore_ascii_case("inf") { return None; }
    t.parse::<rust_decimal::Decimal>().ok()
}

Type guard

fn is_decimal_string(s: &str) -> bool { !s.trim().is_empty() && s.trim().parse::<rust_decimal::Decimal>().is_ok() }

Try / catch

let v = parse_decimal(raw, field).unwrap_or_else(|e| {
    log::warn!("defaulting {field} to zero: {e:#}");
    Decimal::ZERO
});

Prevention

When it happens

Trigger: Any caller (parse_spot/linear/inverse_instrument, parse_funding_rate, parse_price_with_precision, parse_quantity_with_precision) supplies a string Decimal::from_str cannot parse: empty, non-numeric text, or malformed decimals.

Common situations: Bybit returning empty strings for optional/unset fields (common for funding interval or basis rate on newly listed symbols); mis-parsed JSON nesting causing a wrong value type string; test fixtures with dummy values; NaN/Infinity strings.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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