{"record":{"id":"ca19390cfaa17857","repo":"nautechsystems/nautilus_trader","slug":"failed-to-allocate-lighter-nonce-e","errorCode":null,"errorMessage":"failed to allocate Lighter nonce: {e}","messagePattern":"failed to allocate Lighter nonce: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"crates/adapters/lighter/src/execution.rs","lineNumber":1485,"sourceCode":"        let nonce_guard = Arc::clone(&self.nonce_submission_gate)\n            .try_read_owned()\n            .context(\"Lighter nonce refresh is in progress\")?;\n        anyhow::ensure!(\n            self.ws_client.connection_epoch() == connection_epoch\n                && self.nonce_ready_connection_epoch.load(Ordering::Acquire) == connection_epoch,\n            \"Lighter connection or nonce state changed during transaction preparation\",\n        );\n        let nonce = match self\n            .dispatch\n            .nonce_manager\n            .next_nonce(credential.account_index(), credential.api_key_index())\n        {\n            Ok(nonce) => nonce,\n            Err(e @ NonceError::SkipWindowExhausted { .. }) => {\n                // Lost acks leave the baseline stale; resync from the venue so\n                // later commands recover. The fetch is async; this command fails.\n                self.spawn_nonce_window_recovery(credential);\n                anyhow::bail!(\"failed to allocate Lighter nonce: {e}\");\n            }\n            Err(e) => anyhow::bail!(\"failed to allocate Lighter nonce: {e}\"),\n        };\n\n        let now_ns = self.clock.get_time_ns().as_u64() as i64;\n        let expired_at = (now_ns / 1_000_000).saturating_add(DEFAULT_TX_EXPIRY_MS);\n        let send_reservation = self\n            .tx_send_sequencer\n            .reserve(\n                credential.account_index(),\n                credential.api_key_index(),\n                nonce,\n            )\n            .with_nonce_ownership(nonce_guard, connection_epoch);\n\n        let context = TxContext {\n            account_index: credential.account_index(),\n            api_key_index: credential.api_key_index(),","sourceCodeStart":1467,"sourceCodeEnd":1503,"githubUrl":"https://github.com/nautechsystems/nautilus_trader/blob/18893faf8b356be3320add8de2f861b0b647cf06/crates/adapters/lighter/src/execution.rs#L1467-L1503","documentation":"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.","triggerScenarios":"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).","commonSituations":"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.","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"],"exampleFix":"// before: immediate retry loop burning more nonces\nfor _ in 0..3 { client.update_leverage(...).await?; }\n// after: await recovery, then retry once\nif let Err(e) = client.update_leverage(...).await {\n    if e.to_string().contains(\"failed to allocate Lighter nonce\") {\n        tokio::time::sleep(Duration::from_secs(2)).await; // allow nonce recovery to resync\n        client.update_leverage(...).await?;\n    }\n}","handlingStrategy":"retry","validationCode":"// Rust: avoid issuing the command while nonce recovery is in flight\nif !execution.nonce_recovery_in_progress() {\n    execution.update_leverage(params).await?;\n}","typeGuard":null,"tryCatchPattern":"match execution.update_leverage(params).await {\n    Err(e) if e.to_string().contains(\"failed to allocate Lighter nonce\") => {\n        // recovery was spawned server-side; wait then retry once\n        tokio::time::sleep(Duration::from_secs(2)).await;\n        execution.update_leverage(params).await?;\n    }\n    r => r?,\n}","preventionTips":["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"],"tags":["nonce","order-execution","rust","resync","venue"],"backgroundTag":"invalid-state-transition","analyzedSha":"18893faf8b356be3320add8de2f861b0b647cf06","analyzedAt":"2026-09-08T20:49:34.690Z","contentChangedAt":"2026-09-08T20:49:34.690Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}