FuelLabs/fuel-core · error · Error::DB
while getting receipts for tx_id: {:?}
Error message
while getting receipts for tx_id: {:?} What it means
Thrown by the block aggregator API's old-block source while streaming historical blocks: for each transaction in a block, StorageIterator::get_receipts calls the configured TxReceipts provider, and any failure is wrapped into Error::DB with the failing tx_id attached as context (crates/services/block_aggregator_api/src/blocks/old_block_source.rs:156). The error becomes an Err item in the iterator returned by blocks_starting_from, so block streaming stops at the height whose receipts cannot be read. The anyhow context preserves the underlying storage error as root cause.
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source.rs:156
Some(tx) => {
tracing::debug!("found tx id: {:?}", tx_id);
txs.push(tx.into_owned());
}
None => {
return Ok(vec![]);
}
}
}
Ok(txs)
}
fn get_receipts(&self, tx_ids: &[TxId]) -> Result<Vec<Vec<Receipt>>> {
use itertools::Itertools;
tx_ids
.iter()
.map(|tx_id| {
self.receipts.get_receipts(tx_id).map_err(|err| {
Error::DB(anyhow::anyhow!(err).context(format!(
"while getting receipts for tx_id: {:?}",
tx_id
)))
})
})
.try_collect()
}
}
impl<Convertor, DB, Receipts> Iterator for StorageIterator<Convertor, DB, Receipts>
where
DB: StorageInspect<FuelBlocks, Error = StorageError>,
DB: StorageInspect<Transactions, Error = StorageError>,
Receipts: TxReceipts,
Convertor: BlockConverter,
{
type Item = Result<(BlockHeight, Convertor::Block)>;
View on GitHub (pinned to b9d4d170da)
Solutions
- Take the tx_id from the message and probe the receipts storage directly for that key to confirm whether the data exists or the read itself fails.
- Inspect the inner error chain (the anyhow context keeps the root cause) to distinguish 'not found' from IO/lock errors, and fix the storage layer accordingly.
- Verify the node version matches the version that produced the database snapshot and that receipts were not pruned; re-sync the range if data is absent.
- For transient IO/lock failures, restart the stream from the failing height (the iterator stops on Err, so restart rather than continue).
- If your protocol tolerates gaps, skip the affected height explicitly; the iterator itself will not advance past an Err item.
Example fix
// before: stream until it silently stops on the first Err
for item in source.blocks_starting_from(start) {
let (height, block) = item?;
publish(height, block);
}
// after: surface DB receipt failures with height and retry from the same height
let mut height = start;
'outer: loop {
for item in source.blocks_starting_from(height) {
match item {
Ok((h, block)) => { publish(h, block); height = h.succ().unwrap_or(h); }
Err(Error::DB(ctx)) => {
tracing::error!(%ctx, from = ?height, "receipts unavailable");
continue 'outer; // retry stream at same height after fixing storage
}
Err(e) => break 'outer,
}
}
break;
} Defensive patterns
Strategy: retry
Validate before calling
// Before starting the stream, verify receipts are readable for the start height's block
use fuel_core_storage::StorageInspect;
use fuel_core_types::fuel_tx::TxId;
fn receipts_available<R: TxReceipts>(receipts: &R, tx_ids: &[TxId]) -> bool {
tx_ids.iter().all(|id| receipts.get_receipts(id).is_ok())
} Try / catch
match source.blocks_starting_from(height).next() {
Some(Ok((h, block))) => { /* forward block */ }
Some(Err(Error::DB(ctx))) => {
tracing::error!(%ctx, from = ?height, "receipts unavailable");
// restart stream at the same height after fixing/backing off; do not silently skip
restart_stream_at(height);
}
Some(Err(other)) => return Err(other),
None => { /* stream end */ }
} Prevention
- Sync or restore the full database (including receipts) before serving old blocks; never point the old-block source at a pruned snapshot.
- Run producer and consumer nodes on the same fuel-core version so storage layouts match.
- Wrap the TxReceipts implementation with logging so the root cause (IO vs not-found vs closed handle) is recorded before the Error::DB wrap.
- Monitor the stream for Err items and alert instead of letting iteration stop silently.
When it happens
Trigger: Calling blocks_starting_from(height) (directly or via the aggregator's block-serving loop) when the receipts provider fails for one of the block's tx_ids: receipts table missing or pruned for that range, database handle closed or unopened, IO/corruption error inside the provider, or a test stub TxReceipts implementation that returns Err.
Common situations: Node restored from a pruned/partial snapshot that lacks receipt data; underlying DB (e.g. RocksDB) opened with an incompatible column-family layout by a different fuel-core version; disk or IO failures mid-stream; custom Receipts adapters in tests or plugins that error instead of returning data.
Related errors
- Historical execution is only supported with RocksDB
- The historical view is not implemented for `MemoryStore`
- Missing receipt variant
- Could not convert return data digest to Bytes32: {}
- Missing panic reason
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/915dac65f42587a4.
Report an issue: GitHub.