nautechsystems/nautilus_trader · error · anyhow::Error
Failed to parse avg_px='{}': {}
Error message
Failed to parse avg_px='{}': {} What it means
For quote-quantity market orders without a usable limit price, the parser uses the average fill price msg.avg_px as the conversion price (when it is non-empty and not "0"). If avg_px is present but fails Decimal::from_str, this error is raised. It means the reported average fill price on the order update is malformed or non-numeric.
Source
Thrown at crates/adapters/okx/src/websocket/parse.rs:1803
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 {
// No price available, can't convert - use sz as-is temporarily
// This will be corrected once the order gets filled and price is available
parse_quantity(&msg.sz, size_precision)?
};
View on GitHub (pinned to 18893faf8b)
Solutions
- Log the raw msg.avg_px to see the offending value.
- Treat unparseable avg_px as absent (None) so conversion falls back to using sz as-is, matching the no-price branch.
- Trim/normalize the string before Decimal::from_str.
- Fix corrupt fixtures or check for an OKX payload format change.
Example fix
// before (parse.rs:1802)
Some(Decimal::from_str(&msg.avg_px).map_err(|e| {
anyhow::anyhow!("Failed to parse avg_px='{}': {}", msg.avg_px, e)
})?)
// after
match Decimal::from_str(msg.avg_px.trim()) {
Ok(avg) => Some(avg),
Err(e) => {
tracing::warn!(avg_px = %msg.avg_px, "Unparseable avg_px, skipping conversion: {e}");
None // fall back to using sz as-is until a fill price is available
}
} Defensive patterns
Strategy: validation
Validate before calling
// Rust: validate avg_px is a usable conversion price before relying on it
fn usable_avg_px(avg_px: &str) -> Option<Decimal> {
if avg_px.is_empty() || avg_px == "0" {
return None;
}
Decimal::from_str(avg_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 avg_px=") => {
tracing::warn!(ord_id = %msg.ord_id, avg_px = %msg.avg_px, "malformed avg_px on order update");
}
Err(e) => return Err(e),
} Prevention
- Fall back gracefully: an unparseable avg_px should behave like "0" (no conversion price), not abort parsing.
- Validate avg_px in any synthetic OKXOrderMsg used in tests or replay tooling.
- Re-verify recorded fixtures after OKX API upgrades for numeric field format changes.
- Centralize numeric-string parsing in one defensive helper with trim + error logging.
When it happens
Trigger: A quote-quantity market order update (SPOT BUY with tgtCcy=quote_ccy or the tgt_ccy-absent heuristic) where px is the market sentinel and avg_px is a non-empty, non-"0" string that fails Decimal::from_str — e.g. whitespace, corrupt value from a malformed/replayed message.
Common situations: Replaying recorded WebSocket sessions with corrupt avg_px fields; hand-built test OKXOrderMsg values with invalid avg_px; an OKX API format change for avgPx on certain order categories.
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 px='{}': {}
- Failed to parse sz='{}' as quote quantity: {}
- instrument update lock poisoned
- option_summary_family_subs mutex poisoned
- Conditional order types must use OKXAlgoOrderType
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/90bda319503692f7.
Report an issue: GitHub.