nautechsystems/nautilus_trader · error · anyhow::Error
Execution intent {intent_id} has more than one current hash
Error message
Execution intent {intent_id} has more than one current hash What it means
current_execution_hash() found two or more rows with current = TRUE among an intent's transaction hashes, violating the single-current invariant maintained through replace operations (a replacement tx supersedes the previous one). With multiple candidates the reconciler cannot decide which hash to track, so it refuses rather than guessing.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:1833
"Canonical head changed during signer-nonce replacement scan"
);
Ok(found)
}
fn release_slot(&self) {
*self.in_flight.lock().expect("in-flight mutex poisoned") = None;
}
}
fn current_execution_hash(
intent_id: i64,
hashes: &[ExecutionTransactionHashRow],
) -> anyhow::Result<&ExecutionTransactionHashRow> {
let mut current = hashes.iter().filter(|row| row.current);
let row = current
.next()
.ok_or_else(|| anyhow::anyhow!("Execution intent {intent_id} has no current hash"))?;
anyhow::ensure!(
current.next().is_none(),
"Execution intent {intent_id} has more than one current hash"
);
Ok(row)
}
/// Polls for the receipt of a broadcast transaction until it exists or the poll bound
/// is exhausted. A `null` receipt result is a legitimate pending response.
#[cfg(test)]
async fn poll_for_receipt(
http_rpc_client: &BlockchainHttpRpcClient,
tx_hash: &B256,
max_polls: u32,
interval: Duration,
) -> anyhow::Result<Option<RpcTransactionReceipt>> {
let mut last_error = None;
let mut observed_pending = false;
View on GitHub (pinned to 2114cf6f76)
Solutions
- List the duplicates: SELECT id, transaction_hash, status, current FROM execution_transaction_hash WHERE intent_id = <id> AND current ORDER BY id;
- Verify on-chain which transaction is canonical (latest with the intent's nonce) and set current = FALSE on the others
- Ensure only one NautilusTrader client instance per wallet+database runs at a time
- Re-run reconciliation after the repair
Example fix
-- before: two rows flagged current -- id=101 current=t, id=105 current=t -- after: keep only the on-chain-confirmed latest hash as current UPDATE execution_transaction_hash SET current = (id = 105) WHERE intent_id = 42;
Defensive patterns
Strategy: try-catch
Validate before calling
-- Detect duplicate current flags before they block reconciliation SELECT intent_id, count(*) AS current_rows FROM execution_transaction_hash WHERE current GROUP BY intent_id HAVING count(*) > 1; -- Expect zero rows on a healthy store.
Try / catch
try:
client.connect()
except Exception as e:
if 'more than one current hash' in str(e):
# verify which tx is canonical on-chain (intent nonce), keep only that row current,
# set current = FALSE on the others, then reconnect
...
raise Prevention
- Run exactly one execution client per wallet + database; concurrent writers corrupt the current flag
- Include the duplicate-current-hash check in routine DB health checks
When it happens
Trigger: Reconciliation loading an intent whose execution_transaction_hash rows contain several current = TRUE entries: concurrent writers racing on the flag update, a failed transaction around the supersede step, or manual SQL leaving extra rows flagged.
Common situations: Two client instances sharing one database/wallet (unsupported); crash between clearing the old flag and committing the new one wrapped in separate transactions; hand-run UPDATE statements during incident recovery.
Related errors
- Execution intent {intent_id} has no current hash
- Execution intent {} has unknown purpose {}
- Active execution intent {} has no nonce
- Execution transaction {transaction_hash} conflicts with its
- Execution transaction hash {transaction_hash} was not found
AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21).
Data as JSON: /api/errors/97fcb86a539ef9e2.
Report an issue: GitHub.