nautechsystems/nautilus_trader · error
Lighter connection or nonce state changed during transaction
Error message
Lighter connection or nonce state changed during transaction preparation
What it means
A TOCTOU guard in build_tx_context: after acquiring the nonce submission gate, the code re-checks that the connection epoch and nonce-ready epoch still match those observed at entry. If the WS reconnected or a nonce refresh began mid-preparation, the reserved context would be built on stale nonce state, so preparation is aborted.
Source
Thrown at crates/adapters/lighter/src/execution.rs:1470
}
// Per-order `params["market_order_slippage_bps"]` overrides the config default.
fn resolve_slippage_bps(&self, params: Option<&Params>) -> u32 {
params
.and_then(|p| p.get_u64("market_order_slippage_bps"))
.map_or(self.config.market_order_slippage_bps, |v| v as u32)
}
fn build_tx_context(&self, credential: &Credential) -> anyhow::Result<ReservedTxContext> {
let connection_epoch = self.ws_client.connection_epoch();
anyhow::ensure!(
self.nonce_ready_connection_epoch.load(Ordering::Acquire) == connection_epoch,
"Lighter nonce refresh is pending for connection epoch {connection_epoch}",
);
let nonce_guard = Arc::clone(&self.nonce_submission_gate)
.try_read_owned()
.context("Lighter nonce refresh is in progress")?;
anyhow::ensure!(
self.ws_client.connection_epoch() == connection_epoch
&& self.nonce_ready_connection_epoch.load(Ordering::Acquire) == connection_epoch,
"Lighter connection or nonce state changed during transaction preparation",
);
let nonce = match self
.dispatch
.nonce_manager
.next_nonce(credential.account_index(), credential.api_key_index())
{
Ok(nonce) => nonce,
Err(e @ NonceError::SkipWindowExhausted { .. }) => {
// Lost acks leave the baseline stale; resync from the venue so
// later commands recover. The fetch is async; this command fails.
self.spawn_nonce_window_recovery(credential);
anyhow::bail!("failed to allocate Lighter nonce: {e}");
}
Err(e) => anyhow::bail!("failed to allocate Lighter nonce: {e}"),
};View on GitHub (pinned to 18893faf8b)
Solutions
- Retry the operation from scratch — the next attempt will run against the settled connection state.
- Pause command dispatch during reconnects and resume only after readiness is re-signaled.
- Avoid issuing signed txs from multiple tasks concurrently at reconnect boundaries.
- If persistent, investigate WS stability (keepalives, proxy timeouts).
Example fix
// before
let ctx = execution.prepare_signed_modify_order(&cmd, &credential)?;
// after
let ctx = loop {
match execution.prepare_signed_modify_order(&cmd, &credential) {
Ok(ctx) => break ctx,
Err(e) if e.to_string().contains("state changed") => {
tokio::time::sleep(Duration::from_millis(100)).await;
}
Err(e) => return Err(e),
}
}; Defensive patterns
Strategy: retry
Try / catch
match result {
Err(e) if e.to_string().contains("state changed during transaction preparation") => retry_with_backoff(),
other => other,
} Prevention
- Avoid issuing signed txs concurrently during reconnect windows
- Use a single command queue serialized behind connection readiness
- Retry idempotent preparation steps on this transient error
When it happens
Trigger: Concurrent reconnect/nonce refresh racing with any of update_leverage, prepare_signed_modify_order, or prepare_integrator_auto_approval: the epoch or nonce_ready_connection_epoch changed between the first check and gate acquisition.
Common situations: High-frequency order management during unstable network; multiple tasks issuing signed commands at reconnect time; automated loops calling modify/leverage APIs continuously across reconnect boundaries.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- Lighter nonce refresh is pending for connection epoch {conne
- Failed to connect after {max_attempts} attempts
- Failed to connect to {} after {} attempts: {}. If this is a
- error.to_string()
- Failed to fetch order status: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/44df320a7a9fb9a4.
Report an issue: GitHub.