nautechsystems/nautilus_trader · error · anyhow::Error

Finalized Swap log has no block hash

Error message

Finalized Swap log has no block hash

What it means

The library extracts the block hash from the finalized Swap RPC log and requires it to be present before cross-checking the log against the transaction receipt. A missing block_hash field means the node returned an incomplete log object, so consistency verification cannot proceed.

Source

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

            !log.removed
                && log.topics.first().is_some_and(|topic| topic == &signature)
                && Address::from_str(&log.address).ok() == Some(plan.pool_address)
        })
        .collect::<Vec<_>>();
    anyhow::ensure!(
        swap_logs.len() == 1,
        "Finalized transaction {} emitted {} Swap logs from expected pool {}; expected exactly one",
        included.tx_hash,
        swap_logs.len(),
        plan.pool_address
    );
    let log = swap_logs[0];
    let log_transaction_hash = B256::from_str(&rpc_log::extract_transaction_hash(log)?)
        .with_context(|| "Invalid finalized Swap log transaction hash")?;
    let log_block_hash = log
        .block_hash
        .as_deref()
        .ok_or_else(|| anyhow::anyhow!("Finalized Swap log has no block hash"))?;
    anyhow::ensure!(
        log_transaction_hash == included.tx_hash
            && rpc_log::extract_block_number(log)? == included.block_number
            && u64::from(rpc_log::extract_transaction_index(log)?)
                == included.receipt.transaction_index
            && B256::from_str(log_block_hash)
                .with_context(|| "Invalid finalized Swap log block hash")?
                == included.receipt.block_hash,
        "Finalized Swap log position does not match transaction {}",
        included.tx_hash
    );

    let dex = crate::exchanges::get_dex_extended(plan.pool.chain.name, &plan.pool.dex.name)
        .ok_or_else(|| {
            anyhow::anyhow!(
                "No RPC Swap decoder for {}:{}",
                plan.pool.chain.name,
                plan.pool.dex.name

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Use a full-featured RPC provider that returns complete log objects including blockHash
  2. Re-fetch the log via the transaction receipt (eth_getTransactionReceipt) which always includes blockHash
  3. If using middleware/proxy that filters fields, disable field stripping
  4. In test fixtures, populate block_hash before invoking verification
Defensive patterns

Strategy: validation

Validate before calling

fn log_has_block_hash(log: &rpc_log::Log) -> bool { log.block_hash.as_deref().map(|h| h.len() == 66).unwrap_or(false) }
if !log_has_block_hash(log) { return Err(anyhow!("provider returned log without blockHash")); }

Type guard

fn has_block_hash(log: &rpc_log::Log) -> Option<&str> { log.block_hash.as_deref().filter(|h| B256::from_str(h).is_ok()) }

Try / catch

let log = fetch_complete_log(tx_hash).await
    .map_err(|e| if is_incomplete_log(&e) { fetch_from_receipt(tx_hash).await? } else { e })?;

Prevention

When it happens

Trigger: An RPC provider (eth_getLogs or receipt log entry) returning a log JSON without a blockHash field — common with some lightweight or non-standard providers, reorg-pruned responses, or hand-built/mocked log fixtures.

Common situations: Switching RPC providers where one omits blockHash on filtered logs; custom indexing middleware stripping fields; unit-test fixtures built with partial log structs; calling eth_getLogs on a node with partial trace support.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


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