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

Could not convert log data digest to Bytes32: {}

Error message

Could not convert log data digest to Bytes32: {}

What it means

Thrown while decoding a LogData receipt: the digest bytes field must be exactly 32 bytes for fuel's Bytes32; a different length fails try_from and is wrapped as Error::Serialization, aborting the receipt and block conversion.

Source

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

            )
        }
        ProtoReceiptVariant::Revert(revert) => {
            let id = ContractId::try_from(revert.id.as_slice())
                .map_err(|e| Error::Serialization(anyhow!(e)))?;
            Ok(FuelReceipt::revert(id, revert.ra, revert.pc, revert.is))
        }
        ProtoReceiptVariant::Log(log) => {
            let id = ContractId::try_from(log.id.as_slice())
                .map_err(|e| Error::Serialization(anyhow!(e)))?;
            Ok(FuelReceipt::log(
                id, log.ra, log.rb, log.rc, log.rd, log.pc, log.is,
            ))
        }
        ProtoReceiptVariant::LogData(log) => {
            let id = ContractId::try_from(log.id.as_slice())
                .map_err(|e| Error::Serialization(anyhow!(e)))?;
            let digest = Bytes32::try_from(log.digest.as_slice()).map_err(|e| {
                Error::Serialization(anyhow!(
                    "Could not convert log data digest to Bytes32: {}",
                    e
                ))
            })?;
            Ok(FuelReceipt::log_data_with_len(
                id,
                log.ra,
                log.rb,
                log.ptr,
                log.len,
                digest,
                log.pc,
                log.is,
                log.data.clone(),
            ))
        }
        ProtoReceiptVariant::Transfer(transfer) => {
            let id = ContractId::try_from(transfer.id.as_slice())

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Log the digest length of the failing LogData receipt.
  2. Pre-validate len == 32 for all receipt digest fields before decoding.
  3. Pin matching fuel-core versions on both ends.
  4. Reject and re-fetch the block.

Example fix

// before
let digest = Bytes32::try_from(log.digest.as_slice()).map_err(|e| {
    Error::Serialization(anyhow!("Could not convert log data digest to Bytes32: {}", e))
})?;

// after
if log.digest.len() != 32 {
    return Err(Error::Serialization(anyhow::anyhow!(
        "LogData digest length {} != 32", log.digest.len()
    )));
}
Defensive patterns

Strategy: validation

Validate before calling

fn log_data_digest_valid(log: &ProtoLogData) -> bool {
    log.digest.len() == 32
}

Type guard

fn is_bytes32(b: &[u8]) -> bool { b.len() == 32 }

Try / catch

match receipt_from_proto(r) {
    Ok(receipt) => out.push(receipt),
    Err(Error::Serialization(ctx)) if ctx.to_string().contains("log data digest") => {
        tracing::warn!(len = log.digest.len(), "rejecting LogData with bad digest");
        return Err(Error::Serialization(ctx));
    }
    Err(e) => return Err(e),
}

Prevention

When it happens

Trigger: fuel_block_from_protobuf on a block where a LogData receipt's digest has a length other than 32 - corruption, truncation, or an encoder that does not fix the digest size.

Common situations: Producer/consumer version mismatch; corrupted transfer; hand-built receipts in tests.

Related errors


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