nautechsystems/nautilus_trader · error

Retained execution history has multiple active signer owners

Error message

Retained execution history has multiple active signer owners

What it means

During recovery of durable execution state (the retained snapshot of signer intents), the client requires at most one intent to be marked active, since a signer can only have one in-flight transaction. Multiple active intents mean the persisted history is corrupt or was produced by concurrent writers, so recovery is refused rather than guessing which intent is live.

Source

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

        snapshot: ExecutionVerificationMigrationSnapshot,
        finalized: VerifiedBlockHeader,
        finalized_headers: &[VerifiedBlockHeader],
        nonce_verification: &Verified<u64>,
    ) -> anyhow::Result<ExecutionVerificationMigration> {
        let next_canonical_nonce = nonce_verification.value;
        let mut hashes_by_intent: HashMap<i64, Vec<&ExecutionTransactionHashRow>> = HashMap::new();
        for hash in &snapshot.hashes {
            hashes_by_intent
                .entry(hash.intent_id)
                .or_default()
                .push(hash);
        }
        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(|| {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Stop all clients sharing the wallet/storage, inspect the snapshot, and deactivate stale intents so at most one remains active
  2. Delete or archive the corrupted snapshot to force a clean rebuild of execution history (verify no transaction is actually in flight on-chain first)
  3. Ensure only one process per wallet writes to the durable store, and make activate/deactivate transitions atomic
Defensive patterns

Strategy: try-catch

Validate before calling

fn snapshot_recoverable(snapshot: &ExecutionSnapshot) -> Result<(), String> {
    let active = snapshot.intents.iter().filter(|i| i.active).count();
    if active > 1 { Err(format!("{active} active intents in snapshot".into())) } else { Ok(()) }
}

Try / catch

match client.start_with_snapshot(snapshot) {
    Err(e) if e.to_string().contains("multiple active signer owners") => {
        // halt writers, reconcile snapshot on-chain, rebuild state before retry
    }
    other => other?,
}

Prevention

When it happens

Trigger: Loading a retained execution snapshot at client startup where snapshot.intents contains two or more entries with active == true.

Common situations: Two client instances sharing the same durable storage/wallet concurrently marking intents active; a crash between deactivating an old intent and activating a new one combined with a non-atomic writer; manually edited or partially migrated state files.

Understand the failure class

Background: "This is a bug, please report it": internal invariant violations, unreachable panics, and SNH errors explained — this error's family across 47 libraries.

Related errors


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