FuelLabs/fuel-core · error · Error::Serialization

Missing protobuf versioned_block

Error message

Missing protobuf versioned_block

What it means

fuel_block_from_protobuf unwraps the Block.versioned_block oneof; prost decodes bytes that set no recognized variant into None, and the code maps that to Error::Serialization. This is the outermost structural check: it fires before any header/tx/receipt field is read.

Source

Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:506

            Ok(FuelReceipt::burn(
                sub_id,
                contract_id,
                burn.val,
                burn.pc,
                burn.is,
            ))
        }
    }?;

    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| {

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Check the payload before decode: non-empty, plausible length, and ideally a checksum or length prefix from the sender.
  2. After ProtoBlock::decode, immediately assert versioned_block.is_some() and fail fast with payload metadata (len, first bytes) in the error.
  3. Pin producer and consumer to the same fuel-core/protobuf schema version; keep the crate's serialize_block__roundtrip test green across upgrades.
  4. Treat repeated occurrences as a protocol violation and drop or re-fetch from another peer.

Example fix

// before
let (block, receipts) = fuel_block_from_protobuf(proto_block)?;

// after: guard right after decode
let proto_block = ProtoBlock::decode(&*bytes)
    .map_err(Error::serialization_error)?;
if proto_block.versioned_block.is_none() {
    return Err(Error::Serialization(anyhow::anyhow!(
        "payload of {} bytes has no versioned_block variant; wrong schema or corrupt data",
        bytes.len()
    )));
}
Defensive patterns

Strategy: type-guard

Validate before calling

fn proto_block_has_versioned_payload(b: &ProtoBlock) -> bool {
    b.versioned_block.is_some()
}

// right after decode
let proto_block = ProtoBlock::decode(&*bytes).map_err(Error::serialization_error)?;
assert!(proto_block_has_versioned_payload(&proto_block), "no versioned_block variant");

Type guard

fn proto_block_decodable(b: &ProtoBlock) -> bool {
    matches!(b.versioned_block, Some(ProtoVersionedBlock::V1(_)))
}

Try / catch

match fuel_block_from_protobuf(proto_block) {
    Ok((block, receipts)) => Ok((block, receipts)),
    Err(Error::Serialization(ctx)) if ctx.to_string().contains("versioned_block") => {
        tracing::warn!(len = bytes.len(), "payload has no versioned_block; wrong schema or corrupt");
        fetch_block_from_peer(height) // fallback source
    }
    Err(e) => Err(e),
}

Prevention

When it happens

Trigger: ProtoBlock::decode on empty or wrong-schema bytes succeeds (prost is lenient about unknown fields) but yields versioned_block == None; or the payload was encoded by a fuel-core version whose oneof tags differ from this build.

Common situations: Feeding truncated/garbage payloads into fuel_block_from_protobuf; cross-version block exchange where one side only knows V1 and the other emits a different variant; untrusted input at a network boundary.

Related errors


AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16). Data as JSON: /api/errors/a3cc094f6f54aeca. Report an issue: GitHub.