nautechsystems/nautilus_trader · error · anyhow::Error

failed to resolve positive quantity for current order

Error message

failed to resolve positive quantity for current order {} (order_type={:?}, persistence_type={:?}, price_size={}, bsp_liability={}, size_matched={}, size_remaining={}, size_cancelled={}, size_lapsed={}, size_voided={})

What it means

When parsing a Betfair current-order report, no positive quantity could be derived from the order's fields (order type, price/size, BSP liability, matched/remaining/cancelled/lapsed/voided sizes). The library throws via anyhow::ensure! because downstream execution/reconciliation logic requires a strictly positive quantity.

Solutions

  1. Skip orders with all-zero size fields instead of querying their status, or filter them in the caller.
  2. Check the order's order_type; ensure SP/liability orders carry bsp_liability > 0 in the report.
  3. Log the full order payload and verify Betfair returned real values (no missing size fields).
  4. If this occurs for legitimate states, handle a zero quantity as a terminal/no-op order in reconciliation before calling parse.

Example fix

// before
let report = client.query_order(&bet_id).await?;
// after
let report = client.query_order(&bet_id).await?;
if report.size_cancelled + report.size_lapsed + report.size_voided > Decimal::ZERO
    && report.size_matched == Decimal::ZERO {
    return Ok(None); // never-matched terminal order has no resolvable quantity
}
Defensive patterns

Strategy: validation

Validate before calling

fn has_resolvable_qty(o: &CurrentOrder) -> bool {
    o.bsp_liability > Decimal::ZERO
        || o.size_matched > Decimal::ZERO
        || o.size_remaining > Decimal::ZERO
}

Try / catch

match client.query_order(&bet_id).await {
    Ok(r) => r,
    Err(e) if e.to_string().contains("failed to resolve positive quantity") => {
        log::warn!("order {bet_id} has no resolvable quantity; treating as terminal");
        Default::default()
    }
    Err(e) => return Err(e.into()),
}

Prevention

When it happens

Trigger: query_order or fetch_order_status_reports_snapshot_http receives a current order whose resolved lifecycle quantity is zero or negative and which is not a liability-based order (e.g. market-on-close) with bsp_liability > 0.

Common situations: Fully voided or fully lapsed orders without recorded fills; placeholder BET_DELAY-style orders with size fields all zero; orders whose order_type the quantity resolver does not recognize; Betfair returning reports for orders cancelled before matching.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/4ce4821a8db55d72. Report an issue: GitHub.

Appendix: source

Thrown at crates/adapters/betfair/src/http/parse.rs:85

        OrderStatus::Voided
    } else {
        resolve_order_status(order.status, size_matched, size_closed)
    };

    // Prefer lifecycle sum when price_size.size is zero. Use bsp_liability for
    // on-close orders that report liability without stake/size.
    let total_size = order.price_size.size;
    let lifecycle_qty = size_matched + size_remaining + size_cancelled + size_lapsed + size_voided;
    let qty = if total_size > Decimal::ZERO {
        total_size
    } else if lifecycle_qty > Decimal::ZERO {
        lifecycle_qty
    } else if uses_liability_based_quantity(order) && order.bsp_liability > Decimal::ZERO {
        order.bsp_liability
    } else {
        Decimal::ZERO
    };
    anyhow::ensure!(
        qty > Decimal::ZERO,
        "failed to resolve positive quantity for current order {} \
         (order_type={:?}, persistence_type={:?}, price_size={}, bsp_liability={}, \
         size_matched={}, size_remaining={}, size_cancelled={}, size_lapsed={}, size_voided={})",
        order.bet_id,
        order.order_type,
        order.persistence_type,
        order.price_size.size,
        order.bsp_liability,
        size_matched,
        size_remaining,
        size_cancelled,
        size_lapsed,
        size_voided,
    );
    let quantity = parse_betfair_quantity(qty)?;
    let filled_qty = parse_betfair_quantity(size_matched)?;

View on GitHub (pinned to 18893faf8b)