nautechsystems/nautilus_trader · error
Cannot find contingent order for {client_order_id}
Error message
Cannot find contingent order for {client_order_id} What it means
While validating contingent-order constraints, the engine matched a case where the linked (contingent) order's snapshot was None — i.e. the order referenced via linked_order_ids is not in the cache — and panicked instead of continuing validation. An order that participates in a contingency chain must exist locally; its absence means broken chain state. The panic guards against silently mis-validating OCO/OTO relationships.
Source
Thrown at crates/execution/src/matching_engine/mod.rs:2876
}
if let Some(linked_order_ids) = order.linked_order_ids() {
let contingency_type = order.contingency_type();
for client_order_id in linked_order_ids {
match cache_borrow.order(client_order_id) {
Some(contingent_order)
if matches!(
contingency_type,
Some(ContingencyType::Oco | ContingencyType::Ouo)
) && !order.is_closed()
&& contingent_order.is_closed() =>
{
break 'validate Some(
format!("Contingent order {client_order_id} already closed")
.into(),
);
}
None => panic!("Cannot find contingent order for {client_order_id}"),
_ => {}
}
}
}
}
// Check for valid order quantity precision
if order.quantity().precision != self.instrument.size_precision() {
break 'validate Some(
format!(
"Invalid order quantity precision for order {}, was {} when {} size precision is {}",
order.client_order_id(),
order.quantity().precision,
self.instrument.id(),
self.instrument.size_precision()
)
.into(),
);View on GitHub (pinned to 18893faf8b)
Solutions
- Ensure every order in linked_order_ids is submitted/cached on the same node that validates the chain.
- Enable cache persistence so contingent chains survive restarts, or rebuild links during reconciliation.
- Filter out orders whose linked orders cannot be resolved before dispatching them to the matching engine.
- Avoid evicting/finalizing one member of a chain while siblings are still being validated; update the whole chain atomically.
Example fix
// before order.linked_order_ids = Some(vec![missing_order_id]); // after let linked = linked_ids.into_iter().filter(|id| cache.order(id).is_some()).collect::<Vec<_>>(); order.linked_order_ids = (!linked.is_empty()).then_some(linked);
Defensive patterns
Strategy: validation
Validate before calling
let linked = order.linked_order_ids().unwrap_or_default();
let all_cached = linked.iter().all(|id| engine.order_snapshot(id).is_some());
if !all_cached { return Err(anyhow!("contingent chain incomplete")); } Type guard
fn chain_complete(engine: &OrderMatchingEngine, order: &OrderAny) -> bool {
order.linked_order_ids().map_or(true, |ids| ids.iter().all(|id| engine.order_snapshot(id).is_some()))
} Prevention
- Keep every member of an OCO/OTO chain on the same node and in the same cache.
- Finalize and evict chains atomically, not member-by-member.
- Rebuild contingency links after restarts via reconciliation.
- Filter stale linked_order_ids before order submission.
When it happens
Trigger: Validating an order whose linked_order_ids contains a client_order_id with no entry in the matching-engine cache (order_snapshot returns None) during contingency checks.
Common situations: OCO/OTO chains built on a different node or lost by a cache restart; cancel/replace races where the linked order was already finalized and evicted; adapters replaying orders with stale linked_order_ids.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- OTO parent not found
- Order {client_order_id} not found in cache.
- Matching engine not found for instrument {order_instrument_i
- Matching engine not found for instrument {instrument_id}
- Position id should be generated. Hedging Oms type order matc
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/792b30b4868b97f1.
Report an issue: GitHub.