nautechsystems/nautilus_trader · error · anyhow::Error

Failed to parse px='{}': {}

Error message

Failed to parse px='{}': {}

What it means

When converting a quote-quantity sz to base units, the parser prefers the order's limit price msg.px whenever px is not the market-price sentinel ("-1"). If that px string cannot be parsed as a Decimal, this error is raised. It means the limit price on a quote-quantity order is present but malformed/non-numeric.

Source

Thrown at crates/adapters/okx/src/websocket/parse.rs:1798

    let is_quote_qty_heuristic = msg.tgt_ccy.is_none()
        && (msg.inst_type == OKXInstrumentType::Spot || msg.inst_type == OKXInstrumentType::Margin)
        && msg.side == OKXSide::Buy
        && order_type == OrderType::Market;

    let (quantity, filled_qty) = if is_quote_qty_explicit || is_quote_qty_heuristic {
        // Quote-quantity order: sz is in quote currency, need to convert to base
        let sz_quote_dec = Decimal::from_str(&msg.sz).map_err(|e| {
            anyhow::anyhow!("Failed to parse sz='{}' as quote quantity: {}", msg.sz, e)
        })?;

        // Determine the price to use for conversion
        // Priority: 1) limit price (px) for limit orders, 2) avg_px for market orders
        let conversion_price_dec =
            if !is_market_price(&msg.px) {
                // Limit order: use the limit price (msg.px)
                Some(
                    Decimal::from_str(&msg.px)
                        .map_err(|e| anyhow::anyhow!("Failed to parse px='{}': {}", msg.px, e))?,
                )
            } else if !msg.avg_px.is_empty() && msg.avg_px != "0" {
                // Market order with fills: use average fill price
                Some(Decimal::from_str(&msg.avg_px).map_err(|e| {
                    anyhow::anyhow!("Failed to parse avg_px='{}': {}", msg.avg_px, e)
                })?)
            } else {
                None
            };

        // Convert quote quantity to base: quantity_base = sz_quote / price
        let quantity_base = if let Some(price) = conversion_price_dec {
            if price.is_zero() {
                parse_quantity(&msg.sz, size_precision)?
            } else {
                Quantity::from_decimal_dp(sz_quote_dec / price, size_precision)?
            }
        } else {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Log the raw msg.px value from the failing message to confirm the malformed content.
  2. Treat any non-numeric px the same as the market sentinel (fall through to avg_px conversion).
  3. Trim/normalize the px string before parsing.
  4. Fix test fixtures or message construction that put invalid values in px.

Example fix

// before (parse.rs:1796)
Some(Decimal::from_str(&msg.px)
    .map_err(|e| anyhow::anyhow!("Failed to parse px='{}': {}", msg.px, e))?),

// after
match Decimal::from_str(msg.px.trim()) {
    Ok(px) => Some(px),
    Err(e) => {
        tracing::warn!(px = %msg.px, "Unparseable px, falling back to avg_px: {e}");
        None // let the avg_px branch handle conversion
    }
}
Defensive patterns

Strategy: validation

Validate before calling

// Rust: ensure px is either the market sentinel or a valid decimal before conversion
fn usable_limit_px(px: &str) -> Option<Decimal> {
    if is_market_price(px) {
        None
    } else {
        Decimal::from_str(px.trim()).ok()
    }
}

Type guard

fn parseable_decimal(s: &str) -> Option<Decimal> {
    Decimal::from_str(s.trim()).ok()
}

Try / catch

match parse_order_status_report(&msg, &instrument, account_id, ts_init) {
    Ok(report) => handle(report),
    Err(e) if e.to_string().contains("Failed to parse px=") => {
        tracing::warn!(ord_id = %msg.ord_id, px = %msg.px, "malformed px on order update");
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: A quote-quantity order (tgt_ccy=QuoteCcy or the SPOT BUY market heuristic) whose msg.px is a non-sentinel string that fails Decimal::from_str — e.g. empty but not "-1", whitespace-padded, or otherwise corrupt px on the order update.

Common situations: Corrupt or hand-edited recorded WebSocket fixtures; OKX emitting px in an unexpected format for certain instrument types; tests constructing OKXOrderMsg with an invalid px while leaving the quote-qty path active.

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