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
- Narrow the reconciliation time window (start ts) so fills fit within the page cap.
- Re-run reconciliation periodically so each run has fewer pages to cover.
- Check Lighter API health; a repeating/never-terminating cursor may indicate an exchange-side issue.
- 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
- Reconcile frequently so each pass paginates few pages
- Keep reconciliation time windows within tested bounds
- Track reconciliation lag metrics and alert before hitting the cap
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
- Lighter fill reconciliation repeated cursor `{next}`
- IncompleteOrderReports { reports, detail: detail.into() }
- Failed to fetch fills: {e}
- Lighter position snapshot does not cover the requested instr
- no Lighter market_index for position report instrument {inst
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/8daf3c9e0ef215f0.
Report an issue: GitHub.