nautechsystems/nautilus_trader · error · anyhow::Error

Transaction not found

Error message

Transaction not found

What it means

`get_tx` queries a transaction by hash; if the node responds without a `tx` field, the method bails with 'Transaction not found' instead of returning a Tx. The node answered the RPC but has no TX stored at that hash.

Source

Thrown at crates/adapters/dydx/src/grpc/client.rs:518

    }

    /// Query transaction by hash.
    ///
    /// # Errors
    ///
    /// Returns an error if the query fails.
    pub async fn get_tx(&mut self, hash: &str) -> Result<Tx, anyhow::Error> {
        let req = GetTxRequest {
            hash: hash.to_string(),
        };
        let response = self.tx.get_tx(req).await?.into_inner();

        if let Some(tx) = response.tx {
            // Convert through bytes since the types are incompatible
            let tx_bytes = tx.encode_to_vec();
            Tx::try_from(tx_bytes.as_slice()).map_err(|e| anyhow::anyhow!("{e}"))
        } else {
            anyhow::bail!("Transaction not found")
        }
    }
}

#[cfg(test)]
mod tests {
    use rstest::rstest;

    use super::*;

    #[rstest]
    fn test_height_ordering() {
        let h1 = Height(100);
        let h2 = Height(200);
        assert!(h1 < h2);
        assert_eq!(h1, Height(100));
    }

View on GitHub (pinned to 18893faf8b)

Solutions

  1. Retry with backoff — the TX may simply not be committed yet
  2. Confirm the txhash returned by broadcast_tx is the one queried
  3. Check node pruning settings or query an archive node for old TXs
  4. If the TX never appears, re-check the broadcast result code and re-broadcast if needed

Example fix

// before
let tx = client.get_tx(hash).await?;
// after
let tx = loop {
    match client.get_tx(hash).await {
        Ok(tx) => break tx,
        Err(e) if e.to_string().contains("not found") => {
            tokio::time::sleep(Duration::from_millis(500)).await;
        }
        Err(e) => return Err(e),
    }
};
Defensive patterns

Strategy: retry

Validate before calling

// only query hashes returned by a successful broadcast_tx
let hash: String = broadcast_result?; // bail early if broadcast failed

Try / catch

async fn get_tx_with_retry(c: &Client, h: &str) -> anyhow::Result<Tx> {
    for _ in 0..10 {
        match c.get_tx(h).await {
            Ok(t) => return Ok(t),
            Err(e) if e.to_string().contains("not found") => tokio::time::sleep(Duration::from_millis(500)).await,
            Err(e) => return Err(e),
        }
    }
    anyhow::bail!("tx {h} not found after retries")
}

Prevention

When it happens

Trigger: Querying `get_tx` for a hash that has not been committed yet (broadcast in sync mode, TX still in mempool), a pruned node, or a wrong/typo'd hash.

Common situations: Polling for a TX confirmation immediately after a sync-mode broadcast before block inclusion; querying an archive/old hash on a pruned full node; retrying with a hash from a failed broadcast that never landed.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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