nautechsystems/nautilus_trader · error · anyhow::Error

Failed to parse decimal '{value}': {e}

Error message

Failed to parse decimal '{value}': {e}

What it means

parse_decimal converts a Kraken API string into a fixed-precision Decimal; this error is raised when the string fails Decimal::parse. Empty strings and "0" are normalized to 0 beforehand, so the failure means the exchange returned a value the parser cannot interpret. It is a data-format error, not an arithmetic error.

Source

Thrown at crates/adapters/kraken/src/common/parse.rs:60

        enums::{
            KrakenFuturesOrderEventType, KrakenInstrumentType, KrakenPositionSide,
            KrakenSpotTrigger, KrakenTriggerSignal,
        },
    },
    http::models::{
        AssetPairInfo, FuturesFill, FuturesInstrument, FuturesOpenOrder, FuturesOrderEvent,
        FuturesPosition, FuturesPublicExecution, OhlcData, SpotOrder, SpotTrade,
    },
};

/// Parse a decimal string, handling empty strings and "0" values.
pub fn parse_decimal(value: &str) -> anyhow::Result<Decimal> {
    if value.is_empty() || value == "0" {
        return Ok(dec!(0));
    }
    value
        .parse::<Decimal>()
        .map_err(|e| anyhow::anyhow!("Failed to parse decimal '{value}': {e}"))
}

fn parse_rfc3339_timestamp(value: &str, field: &str) -> anyhow::Result<UnixNanos> {
    value
        .parse::<UnixNanos>()
        .map_err(|e| anyhow::anyhow!("Failed to parse {field}='{value}': {e}"))
}

/// Normalizes a Kraken currency code by stripping the legacy X/Z prefix.
///
/// Kraken uses legacy prefixes for some currencies (e.g., XXBT for Bitcoin, XETH for Ethereum,
/// ZUSD for USD). This function strips those prefixes for consistent lookups.
#[inline]
pub fn normalize_currency_code(code: &str) -> &str {
    code.strip_prefix("X")
        .or_else(|| code.strip_prefix("Z"))
        .unwrap_or(code)
}

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log and inspect the offending raw value to confirm the exact format Kraken returned.
  2. Normalize the input (strip thousands separators, expand scientific notation) before calling parse_decimal.
  3. Use parse_decimal_opt and treat unparseable values as absent rather than failing the whole message.
  4. Update the adapter if a Kraken API format change is the cause.

Example fix

// before
let qty = parse_decimal(fill["vol"])?;
// after
let qty = match parse_decimal_opt(fill["vol"]) {
    Some(d) if d > dec!(0) => d,
    _ => { tracing::warn!(raw = %fill["vol"], "unparseable fill volume, skipping"); return Ok(None); }
};
Defensive patterns

Strategy: validation

Validate before calling

// Rust: pre-validate exchange numeric strings
fn is_parseable_decimal(s: &str) -> bool {
    !s.is_empty() && s != "0" && s.parse::<rust_decimal::Decimal>().is_ok()
}

Try / catch

match parse_decimal(raw) {
    Ok(d) => d,
    Err(e) => { tracing::warn!("bad decimal {e}; skipping record"); return Ok(None); }
}

Prevention

When it happens

Trigger: parse_decimal receiving a non-numeric or badly formatted string (e.g. "1,234.5", "12.3.4", "NaN", locale-formatted values) from parse_decimal_opt, compute_avg_px, or parse_fill_report.

Common situations: Kraken changing a field's format or emitting null-ish placeholders; a caller passing already-localized strings; SDK/API version drift where a field switched from number-string to scientific notation.

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/a9c78c581957f46e. Report an issue: GitHub.