nautechsystems/nautilus_trader · error

Lighter fill reconciliation exceeded {MAX_RECONCILIATION_PAG

Error message

Lighter fill reconciliation exceeded {MAX_RECONCILIATION_PAGES} pages

What it means

Fill reconciliation pages through Lighter trade history with cursors. To prevent infinite pagination (e.g. a cursor loop or a live stream that never reaches the start boundary), the loop is capped at MAX_RECONCILIATION_PAGES pages; exceeding the cap aborts with this error instead of looping forever.

Source

Thrown at crates/adapters/lighter/src/execution.rs:5238

            }
            None => None,
        };

        let auth = build_auth_token_for(credential)
            .context("failed to mint Lighter auth token for fill fetch")?;

        let ts_init = self.clock.get_time_ns();
        let mut reports = Vec::new();
        let mut cursor: Option<String> = None;
        let mut seen_cursors = AHashSet::new();
        let mut seen_in_call = AHashSet::new();
        let mut pages = 0_usize;
        let mut oldest_served: Option<UnixNanos> = None;
        let mut covers_window = true;

        loop {
            pages += 1;
            anyhow::ensure!(
                pages <= MAX_RECONCILIATION_PAGES,
                "Lighter fill reconciliation exceeded {MAX_RECONCILIATION_PAGES} pages",
            );
            let query = Zeroizing::new(LighterTradesQuery {
                authorization: None,
                auth: Some(auth.clone()),
                market_id,
                account_index: Some(credential.account_index()),
                order_index: None,
                sort_by: LighterTradeSortBy::Timestamp,
                sort_dir: Some(LighterSortDirection::Desc),
                cursor: cursor.clone(),
                // The venue's `from` parameter is not a timestamp lower bound
                // and can omit the newest trades when given an epoch value.
                from_timestamp: None,
                ask_filter: None,
                role: None,
                trade_type: None,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Narrow the reconciliation time window (start ts) so fills fit within the page cap.
  2. Re-run reconciliation periodically so each run has fewer pages to cover.
  3. Check Lighter API health; a repeating/never-terminating cursor may indicate an exchange-side issue.
  4. If legitimately needed, raise MAX_RECONCILIATION_PAGES in the adapter source and rebuild.

Example fix

// before
let query = LighterTradesQuery { /* start far back */ .. };
// after
let query = LighterTradesQuery {
    // narrow window so pagination stays within MAX_RECONCILIATION_PAGES
    start_timestamp: Some(now_ms - MAX_WINDOW_MS),
    ..
};
Defensive patterns

Strategy: validation

Validate before calling

let window = now - start;
if window > MAX_RECON_WINDOW { return Err(anyhow!("window too large for one reconciliation pass")); }

Try / catch

match res {
    Err(e) if e.to_string().contains("exceeded") && e.to_string().contains("pages") => split_window_and_reconcile_in_chunks(),
    other => other,
}

Prevention

When it happens

Trigger: Reconciling a fill window so large it needs more than MAX_RECONCILIATION_PAGES pages of Lighter trades, or the exchange returning cursors that never converge to the requested start timestamp.

Common situations: First reconciliation after long downtime with a wide lookback window; very high trade frequency accounts; exchange-side cursor anomalies during API incidents.

Related errors


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