nautechsystems/nautilus_trader · error

Retained execution intent belongs to another signer

Error message

Retained execution intent belongs to another signer

What it means

Each retained execution intent is validated against the current client's chain ID and configured wallet address before its history is replayed. This error is thrown when the snapshot was created for a different chain or a different wallet, meaning the stored intents do not belong to this signer and cannot be safely resumed.

Source

Thrown at crates/adapters/blockchain/src/execution/client.rs:5336

        let active_count = snapshot
            .intents
            .iter()
            .filter(|intent| intent.active)
            .count();
        anyhow::ensure!(
            active_count <= 1,
            "Retained execution history has multiple active signer owners"
        );

        let mut nonce_owners = HashMap::new();
        let mut records = Vec::with_capacity(snapshot.intents.len());
        let finalized_headers = finalized_headers
            .iter()
            .map(durable_verified_header)
            .collect::<Vec<_>>();

        for intent in &snapshot.intents {
            anyhow::ensure!(
                intent.chain_id == self.chain.chain_id
                    && intent.wallet_address == self.config.wallet_address,
                "Retained execution intent belongs to another signer"
            );
            let purpose = TransactionPurpose::parse(&intent.purpose).ok_or_else(|| {
                anyhow::anyhow!("Retained execution intent has an unsupported purpose")
            })?;
            let hashes = hashes_by_intent
                .get(&intent.id)
                .map(Vec::as_slice)
                .unwrap_or_default();
            let current = hashes
                .iter()
                .copied()
                .filter(|hash| hash.current)
                .collect::<Vec<_>>();
            anyhow::ensure!(
                current.len() <= 1,

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Move or delete the stale execution snapshot so the client rebuilds state for the current chain/wallet (after confirming no pending on-chain transactions)
  2. Fix the config so chain_id and wallet_address match the deployment that produced the snapshot
  3. Keep per-chain, per-wallet data directories so snapshots are never reused across deployments

Example fix

// before
wallet_address = "0xOLD"
# reuses old snapshot dir
// after
wallet_address = "0xNEW"
mv data/execution data/execution.old-0xOLD  # fresh snapshot for new signer
Defensive patterns

Strategy: validation

Validate before calling

fn snapshot_matches(snapshot: &ExecutionSnapshot, chain_id: u64, wallet: Address) -> bool {
    snapshot.intents.iter().all(|i| i.chain_id == chain_id && i.wallet_address == wallet)
}

Try / catch

match client.start_with_snapshot(snapshot) {
    Err(e) if e.to_string().contains("belongs to another signer") => { /* archive snapshot, confirm no pending tx, rebuild */ }
    other => other?,
}

Prevention

When it happens

Trigger: Starting the execution client with a snapshot whose intent.chain_id != self.chain.chain_id or intent.wallet_address != self.config.wallet_address.

Common situations: Pointing the client at a new RPC chain/chain ID while reusing an old data directory; changing WALLET_ADDRESS in config without clearing retained state; copying state files between deployments (testnet snapshot used on mainnet).

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


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