{"record":{"id":"85943741dd7f01e0","repo":"nautechsystems/nautilus_trader","slug":"missing-persistence-type-for-order-update","errorCode":null,"errorMessage":"missing persistence type for order update {}","messagePattern":"missing persistence type for order update (.+?)","errorType":"exception","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/adapters/betfair/src/stream/parse.rs","lineNumber":903,"sourceCode":"    if let Some(lsrc) = uo.lsrc {\n        report.cancel_reason = Some(lsrc.to_string());\n    }\n\n    Ok(report)\n}\n\nfn parse_stream_time_in_force(uo: &UnmatchedOrder) -> anyhow::Result<TimeInForce> {\n    match uo.pt {\n        Some(persistence_type) => Ok(TimeInForce::from(persistence_type)),\n        None if matches!(\n            uo.ot,\n            crate::common::enums::StreamingOrderType::LimitOnClose\n                | crate::common::enums::StreamingOrderType::MarketOnClose\n        ) =>\n        {\n            Ok(TimeInForce::AtTheClose)\n        }\n        None => anyhow::bail!(\"missing persistence type for order update {}\", uo.id),\n    }\n}\n\nfn stream_order_quantity(uo: &UnmatchedOrder) -> Decimal {\n    if uo.s > Decimal::ZERO {\n        return uo.s;\n    }\n\n    let lifecycle_qty = uo.sm.unwrap_or(Decimal::ZERO)\n        + uo.sr.unwrap_or(Decimal::ZERO)\n        + uo.sc.unwrap_or(Decimal::ZERO)\n        + uo.sl.unwrap_or(Decimal::ZERO)\n        + uo.sv.unwrap_or(Decimal::ZERO);\n\n    if lifecycle_qty > Decimal::ZERO {\n        return lifecycle_qty;\n    }\n","sourceCodeStart":885,"sourceCodeEnd":921,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/a4b06ed870971b5671d12754ea138a3ab99b1dec/crates/adapters/betfair/src/stream/parse.rs#L885-L921","documentation":"Thrown while converting a Betfair Stream API unmatched-order update (`oc` entry) into a NautilusTrader TimeInForce. Betfair carries order persistence in the `pt` (persistenceType) field; a missing `pt` is only tolerated for 'Limit on close' and 'Market on close' order types, which map to TimeInForce::AtTheClose. For any other order type the adapter cannot decide the TIF and bails, failing the parse of that order update.","triggerScenarios":"Receiving an OCM stream message whose unmatched order has `pt` null/absent while `ot` is a type other than LimitOnClose or MarketOnClose (e.g. a plain LIMIT order without persistenceType).","commonSituations":"Malformed or stale Betfair stream messages; replaying captured stream data from an older API-NG version; test fixtures that omit persistenceType; Betfair schema changes on new order types.","solutions":["Reconnect or re-request the stream - a missing pt on an otherwise normal limit order usually indicates a malformed/lost field in that message","Dump the raw OCM message and verify `ot`/`pt`; if `pt` is genuinely absent for a plain LIMIT order, capture the payload and report it to the adapter maintainers as an upstream data issue","If Betfair now legitimately omits pt for a new order type, extend parse_stream_time_in_force in crates/adapters/betfair/src/stream/parse.rs with an explicit mapping for that type"],"exampleFix":"// before\nlet tif = parse_stream_time_in_force(&uo)?;\n\n// after: skip malformed per-order updates instead of failing the stream\nlet tif = match parse_stream_time_in_force(&uo) {\n    Ok(tif) => tif,\n    Err(e) => {\n        log::warn!(\"skipping unmatched order {}: {e}\", uo.id);\n        continue;\n    }\n};","handlingStrategy":"try-catch","validationCode":"fn has_resolvable_tif(uo: &UnmatchedOrder) -> bool {\n    uo.pt.is_some()\n        || matches!(\n            uo.ot,\n            StreamingOrderType::LimitOnClose | StreamingOrderType::MarketOnClose\n        )\n}\n// before parsing: if !has_resolvable_tif(&uo) { skip or alert }","typeGuard":"fn is_betfair_order_parseable(uo: &UnmatchedOrder) -> bool {\n    uo.pt.is_some() || matches!(uo.ot, StreamingOrderType::LimitOnClose | StreamingOrderType::MarketOnClose)\n}","tryCatchPattern":"match parse_stream_time_in_force(&uo) {\n    Ok(tif) => { /* apply update */ }\n    Err(e) => log::warn!(\"skipping order {} with unparseable TIF: {e}\", uo.id),\n}","preventionTips":["Log raw OCM messages during integration so missing pt fields can be traced","Keep Betfair stream replay fixtures current with the live API version","Warn-and-skip on individual malformed order updates instead of failing the whole stream consumer"],"tags":["betfair","streaming","order-parsing","time-in-force","missing-field"],"backgroundTag":"missing-required-field","analyzedSha":"a4b06ed870971b5671d12754ea138a3ab99b1dec","analyzedAt":"2026-08-16T22:54:50.089Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}