nautechsystems/nautilus_trader · error
failed to allocate Lighter nonce: {e}
Error message
failed to allocate Lighter nonce: {e} What it means
This error is raised by build_tx_context when the Lighter nonce allocator fails to hand out a nonce for a new transaction. Specifically it fires on the NonceError::SkipWindowExhausted branch: the skip window (used when the local nonce baseline is behind the venue) has been exhausted, usually because acks were lost and the local baseline is stale. The command fails immediately, but a background nonce-window recovery is spawned to resync the nonce from the venue so later commands can succeed.
Source
Thrown at crates/adapters/lighter/src/execution.rs:1485
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}"),
};
let now_ns = self.clock.get_time_ns().as_u64() as i64;
let expired_at = (now_ns / 1_000_000).saturating_add(DEFAULT_TX_EXPIRY_MS);
let send_reservation = self
.tx_send_sequencer
.reserve(
credential.account_index(),
credential.api_key_index(),
nonce,
)
.with_nonce_ownership(nonce_guard, connection_epoch);
let context = TxContext {
account_index: credential.account_index(),
api_key_index: credential.api_key_index(),View on GitHub (pinned to 18893faf8b)
Solutions
- Let the automatically spawned nonce-window recovery complete, then re-issue the command — it resyncs the nonce from the venue
- Ensure only one process uses this Lighter API key / account at a time
- Restart the execution client to force a fresh nonce baseline fetch from the venue
- Check network stability so transaction acks are not lost between sends
Example fix
// before: immediate retry loop burning more nonces
for _ in 0..3 { client.update_leverage(...).await?; }
// after: await recovery, then retry once
if let Err(e) = client.update_leverage(...).await {
if e.to_string().contains("failed to allocate Lighter nonce") {
tokio::time::sleep(Duration::from_secs(2)).await; // allow nonce recovery to resync
client.update_leverage(...).await?;
}
} Defensive patterns
Strategy: retry
Validate before calling
// Rust: avoid issuing the command while nonce recovery is in flight
if !execution.nonce_recovery_in_progress() {
execution.update_leverage(params).await?;
} Try / catch
match execution.update_leverage(params).await {
Err(e) if e.to_string().contains("failed to allocate Lighter nonce") => {
// recovery was spawned server-side; wait then retry once
tokio::time::sleep(Duration::from_secs(2)).await;
execution.update_leverage(params).await?;
}
r => r?,
} Prevention
- Dedicate one process per Lighter API key so no other consumer advances the nonce
- Treat nonce-allocation failures as a signal to pause submissions until recovery completes
- Monitor ack loss and reconnect events; resync the nonce after each reconnect
When it happens
Trigger: Calling prepare_integrator_auto_approval, prepare_signed_modify_order, or update_leverage when the local nonce skip window is exhausted — i.e. the venue nonce has advanced past the locally tracked baseline faster than it was resynced (lost acks, reconnects, or another client sharing the same API key spending nonces).
Common situations: Running multiple trading instances against the same Lighter API key, WebSocket acks dropped during network instability, a client restart without nonce rehydration, or long-lived sessions where venue-side nonces drift from the adapter's counter.
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
- Verified finalized transaction count advanced without an act
- Invalid venue {}, expected Blockchain DEX format
- failed to parse any of {input_len} Lighter instruments: {}
- invalid cumulative_quote_qty='{}' for cumulative_filled_qty=
- Execution intent {} is not prepared for canonical nonce {}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/ca19390cfaa17857.
Report an issue: GitHub.