nautechsystems/nautilus_trader · error
Failed to parse spotInUseAmt '{spot_in_use_str}': {e}
Error message
Failed to parse spotInUseAmt '{spot_in_use_str}': {e} What it means
The sibling of the liab parse: parse_spot_margin_position_from_balance parses spot_in_use_amt (trimmed) into a Decimal to detect whether a spot margin position exists. This error is raised when spot_in_use_amt is not a valid decimal number.
Source
Thrown at crates/adapters/okx/src/common/parse.rs:976
size_precision: u8,
ts_init: UnixNanos,
) -> anyhow::Result<Option<PositionStatusReport>> {
// OKX returns empty strings for zero values, normalize to "0" before parsing
let liab_str = if balance.liab.trim().is_empty() {
"0"
} else {
balance.liab.trim()
};
let spot_in_use_str = if balance.spot_in_use_amt.trim().is_empty() {
"0"
} else {
balance.spot_in_use_amt.trim()
};
let liab_dec = Decimal::from_str(liab_str)
.map_err(|e| anyhow::anyhow!("Failed to parse liab '{liab_str}': {e}"))?;
let spot_in_use_dec = Decimal::from_str(spot_in_use_str)
.map_err(|e| anyhow::anyhow!("Failed to parse spotInUseAmt '{spot_in_use_str}': {e}"))?;
// Skip if no margin position (no liability and no spot in use)
if liab_dec.is_zero() && spot_in_use_dec.is_zero() {
return Ok(None);
}
// Check if spotInUseAmt is zero first
if spot_in_use_dec.is_zero() {
// No position if spotInUseAmt is zero (regardless of liability)
return Ok(None);
}
// Position side based on spotInUseAmt sign
let (position_side, quantity_dec) = if spot_in_use_dec.is_sign_negative() {
// Negative spotInUseAmt = sold (short position)
(PositionSide::Short, spot_in_use_dec.abs())
} else {
// Positive spotInUseAmt = bought (long position)View on GitHub (pinned to 18893faf8b)
Solutions
- Log the raw spot_in_use_amt value for the failing currency
- Default empty strings to "0" before parsing so the no-position skip path (returns Ok(None)) works
- Sanitize the balance payload upstream before calling the parser
- Upgrade the OKX adapter in case newer versions normalize this field
Example fix
// before
let spot_in_use_dec = Decimal::from_str(spot_in_use_str).map_err(|e| anyhow!("Failed to parse spotInUseAmt '{spot_in_use_str}': {e}"))?;
// after
let spot_in_use_str = spot_in_use_str.trim();
let spot_in_use_dec = if spot_in_use_str.is_empty() { Decimal::ZERO } else { Decimal::from_str(spot_in_use_str).map_err(|e| anyhow!("Failed to parse spotInUseAmt '{spot_in_use_str}': {e}"))? }; Defensive patterns
Strategy: validation
Validate before calling
let siu = balance.spot_in_use_amt.as_deref().unwrap_or("0").trim();
if siu.is_empty() || rust_decimal::Decimal::from_str(siu).is_err() { /* default to "0" or skip record */ } Type guard
fn valid_spot_in_use(s: &str) -> bool {
let t = s.trim(); !t.is_empty() && rust_decimal::Decimal::from_str(t).is_ok()
} Try / catch
match parse_spot_margin_position_from_balance(&balance, ...) {
Ok(Some(pos)) => handle(pos),
Ok(None) => {},
Err(e) if e.to_string().contains("spotInUseAmt") => log::warn!("bad spot_in_use_amt '{}': {e}", balance.spot_in_use_amt),
Err(e) => return Err(e),
} Prevention
- Trim and default empty spot_in_use_amt to "0" before parsing
- Re-validate balance parsing after OKX API upgrades
- Keep fixtures covering both empty-string and populated fields
- Fail soft (skip record) for non-critical balance entries
When it happens
Trigger: balance.spot_in_use_amt contains an empty, malformed, or non-numeric string in an OKX spot margin balance record, after trimming.
Common situations: Empty strings returned by OKX for currencies without margin usage; API response schema changes; fixtures with placeholder values; custom middleware altering number formatting.
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
- Failed to parse liab '{liab_str}': {e}
- Failed to parse fallback quantity for ord_id={}, sz='{}': {e
- Failed to parse filled quantity for ord_id={}, acc_fill_sz='
- Failed to parse base quantity for ord_id={}, sz='{}': {e}
- Failed to parse position quantity '{}' for instrument {}: {e
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/be163252c7c5f0d0.
Report an issue: GitHub.