nautechsystems/nautilus_trader · error

Unowned replacement hash retains signed bytes

Error message

Unowned replacement hash retains signed bytes

What it means

When replaying retained hashes, a replacement hash that the current client cannot authenticate as its own (not owned by this signer) must carry no signed bytes (raw_transaction / sealed_transaction). This error is thrown if an unowned replacement hash still stores signed transaction bytes, which would be a security risk — signed bytes for another owner should never be retained or replayed.

Source

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

                intent.id
            );
            let current = current.first().copied();
            let mut authenticated = HashMap::new();

            for hash in hashes {
                if hash.payload_expected {
                    let raw = open_execution_payload(
                        self.payload_keys
                            .as_deref()
                            .expect("Postgres execution requires payload keys"),
                        self.payload_policy(),
                        intent,
                        hash,
                        "verification migration",
                    )?;
                    authenticated.insert(hash.id, raw);
                } else {
                    anyhow::ensure!(
                        hash.raw_transaction.is_none() && hash.sealed_transaction.is_none(),
                        "Unowned replacement hash retains signed bytes"
                    );
                }
            }

            if let Some(nonce) = intent.nonce {
                anyhow::ensure!(
                    nonce_owners.insert(nonce, intent.id).is_none(),
                    "Retained execution history has duplicate signer nonce ownership"
                );
            }

            let base_decision = verification_decision(
                nonce_verification,
                Some(finalized.number),
                Some(finalized.number),
            );

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Purge the offending snapshot (or strip raw_transaction/sealed_transaction from unowned hashes) and rebuild execution state
  2. Upgrade/downgrade to the client version that authored the snapshot so ownership verification can authenticate the hashes
  3. Never share durable execution stores between wallets; keep signed bytes encrypted and scoped per signer
Defensive patterns

Strategy: validation

Validate before calling

fn no_signed_bytes_on_unowned(hashes: &[HashRecord], wallet: Address) -> bool {
    hashes.iter().filter(|h| !owned_by(h, wallet))
        .all(|h| h.raw_transaction.is_none() && h.sealed_transaction.is_none())
}

Try / catch

match client.start_with_snapshot(snapshot) {
    Err(e) if e.to_string().contains("retains signed bytes") => { /* quarantine store, strip signed bytes from unowned hashes, rebuild */ }
    other => other?,
}

Prevention

When it happens

Trigger: Recovery encounters hash.current intent whose owner check fails (verification migration path not taken) while hash.raw_transaction or hash.sealed_transaction is Some.

Common situations: Snapshot copied from another deployment/wallet that includes signed raw transactions; a version that persisted signed bytes for replacement hashes before this ownership rule existed; store corruption or tampering.

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/bcb7481cf90cccfb. Report an issue: GitHub.