nautechsystems/nautilus_trader · error

eth_getTransactionByHash returned a mismatched transaction h

Error message

eth_getTransactionByHash returned a mismatched transaction hash

What it means

Safety check in `get_transaction_by_hash`: the transaction returned by `eth_getTransactionByHash` must carry the same `hash` that was requested; otherwise the library bails. It protects callers from RPC nodes returning mismatched transaction objects.

Source

Thrown at crates/adapters/blockchain/src/rpc/http.rs:763

    /// does not match `tx_hash`.
    #[cfg(feature = "hypersync")]
    #[allow(
        dead_code,
        reason = "Used by the independent verification read inventory"
    )]
    pub(crate) async fn get_transaction_by_hash(
        &self,
        tx_hash: &B256,
    ) -> anyhow::Result<Option<RpcTransaction>> {
        let transaction: Option<RpcTransaction> = self
            .execute_execution_rpc_call("eth_getTransactionByHash", serde_json::json!([tx_hash]))
            .await?;

        if transaction
            .as_ref()
            .is_some_and(|transaction| transaction.hash != *tx_hash)
        {
            anyhow::bail!("eth_getTransactionByHash returned a mismatched transaction hash");
        }
        Ok(transaction)
    }

    /// Returns the Geth `callTracer` tree for a transaction.
    ///
    /// # Errors
    ///
    /// Returns an error if the trace method is unavailable or the result is missing or malformed.
    #[cfg(feature = "hypersync")]
    #[allow(
        dead_code,
        reason = "Used by the independent verification read inventory"
    )]
    pub(crate) async fn trace_transaction_call(
        &self,
        tx_hash: &B256,
    ) -> anyhow::Result<RpcCallTrace> {

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Retry against a different, trusted RPC endpoint.
  2. Remove or audit any middleware/proxy that rewrites JSON-RPC responses.
  3. Ensure the requested hash is a well-formed 32-byte hex string so the node isn't forced into fuzzy matching.
  4. Update the node software/provider client if self-hosting.
Defensive patterns

Strategy: retry

Validate before calling

fn valid_tx_hash(h: &str) -> bool { h.len() == 66 && h.starts_with("0x") && h[2..].chars().all(|c| c.is_ascii_hexdigit()) }

Type guard

fn tx_matches(tx: &Transaction, tx_hash: &Hash) -> bool {
    tx.hash == *tx_hash
}

Try / catch

let tx = match rpc.get_transaction_by_hash(tx_hash).await {
    Ok(t) => t,
    Err(e) if e.to_string().contains("mismatched transaction hash") => {
        warn!("integrity check failed; retrying on fallback endpoint");
        fallback_rpc.get_transaction_by_hash(tx_hash).await?
    }
    Err(e) => return Err(e),
};

Prevention

When it happens

Trigger: Calling `get_transaction_by_hash(tx_hash)` and the node returns a transaction object whose `hash` field differs from the requested hash.

Common situations: Faulty RPC provider or proxy returning cached/mutated payloads; custom node software with hash-encoding bugs; race with an indexing middleware rewriting responses.

Understand the failure class

Background: "invalid response format", "malformed payload", "missing data field": when an API returns 200 but the response shape is wrong — this error's family across 23 libraries.

Related errors


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