nautechsystems/nautilus_trader · error

ambiguous Lighter active-order lookup for client_order_index

Error message

ambiguous Lighter active-order lookup for client_order_index {client_order_index} and nonce {nonce}

What it means

During reconciliation, the adapter filters the venue's active orders by (client_order_index, nonce) and requires exactly one match. If two or more active orders share the same client_order_index and nonce, the venue state is ambiguous and the lookup is aborted instead of guessing. This protects Nautilus order state from being built from the wrong venue order.

Source

Thrown at crates/adapters/lighter/src/websocket/dispatch.rs:1762

    let query = Zeroizing::new(LighterAccountActiveOrdersQuery {
        authorization: None,
        auth: Some(auth.clone()),
        account_index: credential.account_index(),
        market_id: market_index,
    });
    let active = http_client
        .get_account_active_orders(&query)
        .await
        .context("failed to fetch Lighter active orders")?;

    let mut matches = active
        .orders
        .iter()
        .filter(|order| order.client_order_index == client_order_index && order.nonce == nonce);
    let Some(order) = matches.next() else {
        return Ok(None);
    };
    anyhow::ensure!(
        matches.next().is_none(),
        "ambiguous Lighter active-order lookup for client_order_index {client_order_index} and nonce {nonce}",
    );

    let report = parse_http_order_to_report(order, registry, account_id, clock.get_time_ns())
        .ok_or_else(|| {
            anyhow::anyhow!(
                "failed to parse Lighter active order {} for acknowledged create",
                order.order_index,
            )
        })?;
    let report = dispatch
        .translate_order_cloid(report)
        .with_client_order_id(client_order_id);
    Ok(Some(dispatch.preserve_pending_order_status(report)))
}

/// Look up a single order via the active and inactive HTTP endpoints, returning

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Fetch the venue open orders and inspect duplicates for that client_order_index; cancel the unwanted duplicate on the venue.
  2. Ensure the nonce used at submit time is unique per order (check nonce generation logic in the signing/submit path).
  3. Re-run reconciliation after cancelling duplicates so the lookup resolves to a single order.

Example fix

// before: reconcile by (client_order_index, nonce) with duplicates on venue
anyhow::ensure!(matches.next().is_none(), "ambiguous ...");
// after: prevent duplicates upstream by generating a strictly increasing unique nonce per CreateOrder tx
Defensive patterns

Strategy: validation

Validate before calling

let dups: Vec<_> = active.orders.iter().filter(|o| o.client_order_index == idx && o.nonce == nonce).collect();
if dups.len() > 1 { /* cancel duplicates before reconciling */ }

Prevention

When it happens

Trigger: Calling the active-order lookup/reconciliation path with a client_order_index+nonce pair that matches more than one order in the exchange's open-orders response (e.g. venue allowed a duplicate client_order_index with a colliding nonce).

Common situations: Reconnecting/reconciling after a network split where a submission was retried and the venue recorded two orders with the same client_order_index and nonce; bugs in client-generated nonce handling.

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/73d9ea755d939f15. Report an issue: GitHub.