nautechsystems/nautilus_trader · error

missing persistence type for order update

Error message

missing persistence type for order update {}

What it means

When converting a Betfair streaming order update into a Nautilus order-status report, the parser maps the order's persistence type (LIMIT order persistence: LAPSE vs PERSIST) to a TimeInForce. If the UnmatchedOrder carries no persistence value, the mapping has nothing to translate and the parser bails with the order id. The adapter cannot represent the update faithfully without it.

Solutions

  1. Re-establish a clean stream snapshot (resubscribe to order changes) so the full order image with persistence is received.
  2. Check the order's type in the update: only LIMIT orders should reach this branch; ensure SP/IOC-style orders are handled by their own mappings.
  3. If the exchange legitimately omits persistence, default it explicitly (e.g. treat as LAPSE/Gtc) instead of failing, or update the adapter mapping for that order type.
  4. Verify the adapter's parse_stream_time_in_force covers all StreamingOrderType variants your account can place.

Example fix

// before
None => anyhow::bail!("missing persistence type for order update {}", uo.id),
// after
None => {
    log::warn!("missing persistence type for order update {}, defaulting to Gtc", uo.id);
    Ok(TimeInForce::Gtc)
}
Defensive patterns

Strategy: fallback

Validate before calling

if uo.order_type == Limit && uo.persistence_type.is_none() {
    log::warn!("order {} update missing persistence; snapshot may be stale", uo.id);
}

Type guard

fn has_persistence(uo: &UnmatchedOrder) -> bool {
    matches!(uo.order_type, StreamingOrderType::Limit) && uo.persistence_type.is_some()
}

Try / catch

match parse_order_status_report(&uo) {
    Err(e) if e.to_string().contains("missing persistence type") => {
        log::warn!("resubscribing to get a full order image");
        resubscribe_orders();
    }
    other => other,
}

Prevention

When it happens

Trigger: Receiving an orderChangeMessage / unmatched order (type Limit) whose persistence field is absent — e.g. via an out-of-order change message (conflate/heartbeat loss of the initial image) or an exchange payload lacking the field for this order type.

Common situations: Connecting to the Betfair stream with conflation enabled so the initial full order image is missed; Betfair changing/omitting persistence on certain order types (e.g. SP or market-on-close orders reaching an unexpected branch); parsing order updates right after reconnect without a resubscribe snapshot.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at crates/adapters/betfair/src/stream/parse.rs:890

    if let Some(lsrc) = uo.lsrc {
        report.cancel_reason = Some(lsrc.to_string());
    }

    Ok(report)
}

fn parse_stream_time_in_force(uo: &UnmatchedOrder) -> anyhow::Result<TimeInForce> {
    match uo.pt {
        Some(persistence_type) => Ok(TimeInForce::from(persistence_type)),
        None if matches!(
            uo.ot,
            crate::common::enums::StreamingOrderType::LimitOnClose
                | crate::common::enums::StreamingOrderType::MarketOnClose
        ) =>
        {
            Ok(TimeInForce::AtTheClose)
        }
        None => anyhow::bail!("missing persistence type for order update {}", uo.id),
    }
}

fn stream_order_quantity(uo: &UnmatchedOrder) -> Decimal {
    if uo.s > Decimal::ZERO {
        return uo.s;
    }

    let lifecycle_qty = uo.sm.unwrap_or(Decimal::ZERO)
        + uo.sr.unwrap_or(Decimal::ZERO)
        + uo.sc.unwrap_or(Decimal::ZERO)
        + uo.sl.unwrap_or(Decimal::ZERO)
        + uo.sv.unwrap_or(Decimal::ZERO);

    if lifecycle_qty > Decimal::ZERO {
        return lifecycle_qty;
    }

View on GitHub (pinned to 18893faf8b)