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
- Inspect the raw value for the named field; confirm it is a finite decimal string.
- Handle empty strings explicitly (default/skip) before calling parse_decimal.
- Trim and normalize (no commas, no 'NaN'/'inf').
- 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
- Handle empty-string optional fields (funding rate, basis rate) before parsing
- Trim all exchange-provided numeric strings
- Cover newly listed symbols (empty optional fields) in tests
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- invalid {field} `{raw}`
- invalid Bybit trigger type: '{s}', expected LastPrice, MarkP
- invalid Bybit TP/SL order type: '{s}', expected Market or Li
- invalid Bybit TP/SL mode: '{s}', expected Full or Partial
- Invalid topic format: empty topic
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/1b94da3adb9ad179.
Report an issue: GitHub.