nautechsystems/nautilus_trader · error
Pending nonce does not match the verified canonical nonce
Error message
Pending nonce does not match the verified canonical nonce
What it means
Before signing, the client reads the account nonce twice: the canonical nonce at the verified decision block and the pending (mempool-inclusive) transaction count. Signing is only safe when they match — i.e. the wallet has no in-flight transactions. A mismatch means pending transactions exist that would collide with the nonce about to be assigned, so the client aborts.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:2428
if let Some(anchors) = swap_anchors {
decisions.extend(self.verify_swap_anchors_before_sign(anchors).await?);
decisions.extend(anchors.precondition_decisions.iter().cloned());
} else {
decisions.extend(self.verify_pre_sign_header_fence(decision_header).await?);
}
let canonical_nonce_verification = required_verification(
self.verification
.verify_transaction_count(&self.wallet_address, decision_header.number)
.await,
"pre-sign canonical nonce reread",
)?;
let pending_nonce_verification = required_verification(
self.verification
.verify_pending_transaction_count(&self.wallet_address)
.await,
"pre-sign pending nonce reread",
)?;
anyhow::ensure!(
pending_nonce_verification.value == canonical_nonce_verification.value,
"Pending nonce does not match the verified canonical nonce"
);
let nonce = canonical_nonce_verification.value;
decisions.push(verification_decision(
&canonical_nonce_verification,
decision_height,
decision_height,
));
decisions.push(verification_decision(
&pending_nonce_verification,
None,
None,
));
let tx = build_eip1559_transaction(
expected_chain_id,
nonce,
gas_limit,View on GitHub (pinned to 18893faf8b)
Solutions
- Wait for the pending transaction(s) to be confirmed (or explicitly replace/cancel them with a higher-priority-fee same-nonce transaction), then retry.
- Ensure a single signing process owns the wallet; add cross-process nonce leasing/locking.
- Retry after the mempool clears; the check is designed to be re-run and will pass once pending == canonical.
- Point the client at a consistent, in-sync RPC endpoint; avoid load-balanced providers with divergent nodes.
Example fix
// before: immediate retry after broadcast, pending tx still in flight let prepared = client.prepare_and_sign(intent).await?; // fails: pending nonce 7 != canonical 6 // after: confirm the previous tx first wait_for_confirmation(previous_tx_hash).await?; let prepared = client.prepare_and_sign(intent).await?;
Defensive patterns
Strategy: retry
Validate before calling
pub async fn nonce_clear(ver: &Verification, wallet: Address) -> anyhow::Result<bool> {
let canonical = ver.verify_transaction_count(&wallet, latest()).await?;
let pending = ver.verify_pending_transaction_count(&wallet).await?;
Ok(pending.value == canonical.value)
} Try / catch
match sign_attempt().await {
Err(e) if e.to_string().contains("Pending nonce does not match") => {
wait_for_pending_confirmation().await?;
sign_attempt().await?
}
other => other?,
} Prevention
- Ensure exactly one signing process owns the wallet key; use nonce leasing.
- Wait for confirmation of a broadcast before preparing the next transaction.
- Use a consistent, in-sync RPC endpoint for both canonical and pending reads.
When it happens
Trigger: Calling the pre-sign path while the wallet has unconfirmed transactions in the mempool (pending nonce > canonical nonce), or a stale/inconsistent RPC where the pending view lags or diverges from the canonical view.
Common situations: A previous broadcast is still unconfirmed (stuck/slow transaction); another process or bot shares the same wallet key and broadcast concurrently; RPC provider returning inconsistent pending vs latest state; retry loop re-signing before confirmation of the earlier tx.
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
- Canonical head changed during signer-nonce replacement scan
- Verified finalized transaction count advanced without an act
- Execution intent {} has a signed transaction {} that was not
- Canonical nonce advanced without an authenticated signer tra
- Canonical nonce ledger changed during verification bootstrap
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/b2c13e753fd40871.
Report an issue: GitHub.