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
- Retry with backoff — the TX may simply not be committed yet
- Confirm the txhash returned by broadcast_tx is the one queried
- Check node pruning settings or query an archive node for old TXs
- 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
- Poll with exponential backoff after sync-mode broadcasts
- Use the exact txhash from the broadcast response
- Query archive nodes for old or pruned transactions
- Distinguish 'not yet committed' from 'permanently absent' before re-broadcasting
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
- Transaction broadcast failed: code={}, log={}
- Failed to start replacement transaction persistence: {e}
- Failed to retire replaced execution hash: {e}
- Failed to mark execution intent replaced: {e}
- Failed to record replacement transition: {e}
AI-assisted analysis of nautechsystems/nautilus_trader@18893faf8b (2026-09-08).
Data as JSON: /api/errors/6e209c0a8d68c349.
Report an issue: GitHub.