nautechsystems/nautilus_trader · error

Signed transaction nonce {} does not match persisted nonce {

Error message

Signed transaction nonce {} does not match persisted nonce {}

What it means

This error is thrown by `validate_signed_transaction` when the nonce recovered from the decoded signed EIP-1559 transaction differs from the nonce persisted in the `SignedTransactionIntent`. The library treats the persisted intent as the sole authoritative record of what was approved to sign, so any divergence between the signed bytes and that record is rejected as a tamper or replay attempt before the transaction is broadcast.

Source

Thrown at crates/adapters/blockchain/src/execution/transaction.rs:282

        intent.intent_chain_id == intent.chain_id,
        "Persisted intent chain ID {} does not match configured chain ID {}",
        intent.intent_chain_id,
        intent.chain_id
    );
    anyhow::ensure!(
        intent.row_chain_id == intent.chain_id,
        "Persisted transaction row chain ID {} does not match configured chain ID {}",
        intent.row_chain_id,
        intent.chain_id
    );

    anyhow::ensure!(
        tx.chain_id == u64::from(intent.chain_id),
        "Signed transaction chain ID {} does not match configured chain ID {}",
        tx.chain_id,
        intent.chain_id
    );
    anyhow::ensure!(
        tx.nonce == intent.nonce,
        "Signed transaction nonce {} does not match persisted nonce {}",
        tx.nonce,
        intent.nonce
    );
    anyhow::ensure!(
        tx.to == intent.to,
        "Signed transaction destination {} does not match persisted destination {}",
        tx.to,
        intent.to
    );
    anyhow::ensure!(
        tx.value == intent.value,
        "Signed transaction value does not match persisted value"
    );
    anyhow::ensure!(
        tx.input == intent.input,
        "Signed transaction calldata does not match persisted calldata"

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Re-sign the transaction so it uses exactly `intent.nonce`, then re-run validation with the new signed bytes and the matching intent.
  2. Verify the persisted intent row is current: if the nonce was legitimately bumped, regenerate/refresh the intent rather than forcing the old signature through.
  3. Check that only one component mutates the nonce; align on a single source (persisted intent) for both signing and validation.
  4. If this is a test failure, regenerate the fixture signature instead of comparing an old signed blob against a new intent.

Example fix

// before
let signed = sign(tx_with_stale_nonce, &wallet);
validate_signed_transaction(&signed, &intent)?; // nonce mismatch

// after
let mut tx = build_tx_from_intent(&intent); // tx.nonce = intent.nonce
let signed = sign(&tx, &wallet);
validate_signed_transaction(&signed, &intent)?;
Defensive patterns

Strategy: validation

Validate before calling

fn nonce_matches(raw: &[u8], intent: &SignedTransactionIntent) -> anyhow::Result<bool> {
    let tx = decode_signed_transaction(raw)?;
    Ok(tx.nonce == intent.nonce)
}

Type guard

fn is_expected_nonce(decoded: &DecodedSignedTransaction, intent: &SignedTransactionIntent) -> bool {
    decoded.nonce == intent.nonce
}

Try / catch

match validate_signed_transaction(&raw, &intent) {
    Ok(()) => submit(raw),
    Err(e) if e.to_string().contains("nonce") => {
        tracing::warn!("stale signature: nonce drift; re-signing from intent");
        re_sign_and_validate(&intent)?;
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: Calling `validate_signed_transaction` (directly or via `authenticate_payload_identity_with_signer`) with raw signed bytes whose recovered nonce does not equal `intent.nonce` — e.g. bytes from a re-signed replacement transaction, a stale signed blob, or bytes whose persisted intent row was regenerated with a different nonce.

Common situations: The signing service re-signed the payload after a nonce was bumped (failed/stuck tx replaced), the intent row was re-created or migrated while an old signed blob remained, two components read different nonce sources (local counter vs node `eth_getTransactionCount`), or a test fixture reuses a signed blob across intents.

Understand the failure class

Background: Checksum mismatch errors: "checksum verification failed", "digest mismatch", "expected vs actual checksum" — what they mean and how to fix them — this error's family across 41 libraries.

Related errors


AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08). Data as JSON: /api/errors/05ee3598db70e8ed. Report an issue: GitHub.