nautechsystems/nautilus_trader · error · anyhow::Error
Persisted terminal transaction {tx_hash} is not stable at th
Error message
Persisted terminal transaction {tx_hash} is not stable at the finalized boundary What it means
For a persisted terminal transaction, receipt_is_stably_finalized() re-derived finality and it failed: either the node's finalized block number is still below the receipt's block number, or re-fetching the receipt's block and the finalized block produced hashes different from the ones first observed. The ensure! converts that instability into a hard error instead of trusting the persisted 'finalized'/'reverted' status.
Source
Thrown at crates/adapters/blockchain/src/execution/client.rs:994
let executor = self.transaction_executor()?;
let prepared = PreparedTransaction {
intent_id: intent.id,
created_block: intent.created_block,
nonce,
tx_hash,
raw_tx: current.raw_transaction.clone().unwrap_or_default(),
};
let outcome = if matches!(intent.status.as_str(), "finalized" | "reverted") {
let receipt = executor
.http_rpc_client
.get_transaction_receipt(&tx_hash)
.await?
.ok_or_else(|| {
anyhow::anyhow!(
"Finalized execution transaction {tx_hash} no longer has a receipt"
)
})?;
anyhow::ensure!(
executor.receipt_is_stably_finalized(&receipt).await?,
"Persisted terminal transaction {tx_hash} is not stable at the finalized boundary"
);
if intent.status == "finalized" {
InclusionOutcome::Finalized(IncludedTransaction {
intent_id: intent.id,
tx_hash,
block_number: receipt.block_number,
receipt,
})
} else {
InclusionOutcome::Reverted(tx_hash)
}
} else {
executor.await_finality(&prepared).await?
};
View on GitHub (pinned to 2114cf6f76)
Solutions
- Retry connect()/reconciliation after the node catches up — finality lag is usually transient
- Query the node directly: eth_getBlockByNumber('finalized') and compare its number/hash against the receipt's blockNumber/blockHash
- If it persists, switch to a reputable, canonical RPC provider and reconcile again
- Only if on-chain state is independently verified should you repair the intent row manually
Example fix
# before: fail-fast single attempt during a finality blip
client.connect() # raises: not stable at the finalized boundary
# after: retry with backoff until the node view stabilizes
for attempt in range(5):
try:
client.connect()
break
except Exception as e:
if "finalized boundary" not in str(e):
raise
time.sleep(10 * (attempt + 1)) Defensive patterns
Strategy: retry
Validate before calling
from web3 import Web3
w3 = Web3(Web3.HTTPProvider(rpc_url))
finalized = w3.eth.get_block('finalized')
receipt = w3.eth.get_transaction_receipt(tx_hash)
stable = (finalized.number >= receipt.blockNumber)
assert stable, 'node finality lags the receipt; retry connect later' Try / catch
for attempt in range(6):
try:
client.connect()
break
except Exception as e:
if 'finalized boundary' not in str(e):
raise
time.sleep(15 * (attempt + 1)) # let the node's finalized tag catch up
else:
raise RuntimeError('reconciliation never stabilized') Prevention
- Use RPC providers with fast, accurate finalized-tag tracking
- Avoid reconnecting mid-network-incident; wait until finality indicators settle
When it happens
Trigger: Startup reconciliation of a 'finalized'/'reverted' intent while the RPC endpoint lags on finality, serves a non-canonical view, or the chain actually reorganized blocks that were previously reported finalized.
Common situations: Aggressively-load-balanced or self-hosted nodes with inconsistent fork state; reconnecting right after a network incident; querying nodes whose 'finalized' tag trails the actual finalized height.
Related errors
- Finalized block {} changed from {} to {} before intent valid
- Finalized execution transaction {tx_hash} no longer has a re
- Finalized block {} changed from {} to {} before fill emissio
- Canonical head changed during signer-nonce replacement scan
- Finalized block {} does not contain transaction {}
AI-assisted analysis of nautechsystems/nautilus_trader@2114cf6f76 (2026-08-21).
Data as JSON: /api/errors/cb1188863131ee0c.
Report an issue: GitHub.