FuelLabs/fuel-core · error · Error::Serialization
Missing protobuf header
Error message
Missing protobuf header
What it means
While decoding a V1 proto block, fuel_block_from_protobuf requires the header sub-message on the V1Block; prost yields None when absent and the code maps it to Error::Serialization. A V1 block without a header cannot produce a FuelBlockHeader, so the decode aborts.
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:512
))
}
}?;
Ok(receipt)
}
pub fn fuel_block_from_protobuf(
proto_block: ProtoBlock,
) -> crate::result::Result<(FuelBlock, Vec<Vec<FuelReceipt>>)> {
let versioned_block = proto_block
.versioned_block
.ok_or_else(|| anyhow::anyhow!("Missing protobuf versioned_block"))
.map_err(Error::Serialization)?;
let (partial_header, event_inbox_root, txs, receipts) = match versioned_block {
ProtoVersionedBlock::V1(v1_inner) => {
let proto_header = v1_inner
.header
.ok_or_else(|| anyhow::anyhow!("Missing protobuf header"))
.map_err(Error::Serialization)?;
let (partial_header, event_inbox_root) =
partial_header_from_proto_header(&proto_header)?;
let txs = v1_inner
.transactions
.iter()
.map(tx_from_proto_tx)
.collect::<crate::result::Result<_>>()?;
let receipts = v1_inner
.receipts
.iter()
.map(|rs| {
rs.receipts
.iter()
.map(receipt_from_proto)
.collect::<crate::result::Result<Vec<_>>>()
})
.collect::<crate::result::Result<Vec<_>>>()?;View on GitHub (pinned to b9d4d170da)
Solutions
- Debug-print the V1Block to confirm header is the only missing piece.
- Fix the producer to always set header (see ProtobufBlockConverter::convert_block, which always attaches Some(proto_header)).
- Pre-validate v1.header.is_some() after decode and reject with block-level context.
- Re-fetch the block from a compatible peer.
Example fix
// before
let (block, receipts) = fuel_block_from_protobuf(proto_block)?;
// after: structural pre-check on the versioned payload
if let Some(ProtoVersionedBlock::V1(v1)) = proto_block.versioned_block.as_ref() {
if v1.header.is_none() {
return Err(Error::Serialization(anyhow::anyhow!(
"V1 block payload missing header"
)));
}
} Defensive patterns
Strategy: type-guard
Validate before calling
fn v1_block_has_header(v1: &ProtoV1Block) -> bool {
v1.header.is_some()
} Type guard
fn v1_block_decodable(v1: &ProtoV1Block) -> bool {
v1.header.is_some()
} Try / catch
match fuel_block_from_protobuf(proto_block) {
Ok((block, receipts)) => Ok((block, receipts)),
Err(Error::Serialization(ctx)) if ctx.to_string().contains("header") => {
tracing::warn!("V1 payload missing header; re-fetching");
fetch_block_from_peer(height)
}
Err(e) => Err(e),
} Prevention
- Encoders must always attach Some(header) (mirror ProtobufBlockConverter::convert_block).
- Structurally validate the versioned payload (header present) before deep conversion.
- Reject blocks from schema-incompatible versions.
When it happens
Trigger: fuel_block_from_protobuf on a ProtoBlock whose V1 payload omitted header - encoder bug, truncated message, or a schema-skewed producer.
Common situations: Version mismatch between fuel-core releases; hand-built test blocks that fill only transactions/receipts; corruption.
Related errors
- Missing utxo_id on contract input
- Missing tx_pointer on contract input
- Missing receipt variant
- Missing panic reason
- Missing script result payload
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/3314da905178a42f.
Report an issue: GitHub.