FuelLabs/fuel-core · error · Error::Serialization
Could not convert message digest to Bytes32: {}
Error message
Could not convert message digest to Bytes32: {} What it means
Thrown while decoding a MessageOut receipt: the digest bytes field must be exactly 32 bytes for Bytes32; any other length fails try_from and is wrapped as Error::Serialization. The error fails the receipt, its transaction, and the block conversion.
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:455
ProtoReceiptVariant::ScriptResult(result) => {
let script_result = result.result.as_ref().ok_or_else(|| {
Error::Serialization(anyhow!("Missing script result payload"))
})?;
let execution_result = script_execution_result_from_proto(script_result)?;
Ok(FuelReceipt::script_result(
execution_result,
result.gas_used,
))
}
ProtoReceiptVariant::MessageOut(msg) => {
let sender = Address::try_from(msg.sender.as_slice())
.map_err(|e| Error::Serialization(anyhow!(e)))?;
let recipient = Address::try_from(msg.recipient.as_slice())
.map_err(|e| Error::Serialization(anyhow!(e)))?;
let nonce = Nonce::try_from(msg.nonce.as_slice())
.map_err(|e| Error::Serialization(anyhow!(e)))?;
let digest = Bytes32::try_from(msg.digest.as_slice()).map_err(|e| {
Error::Serialization(anyhow!(
"Could not convert message digest to Bytes32: {}",
e
))
})?;
Ok(FuelReceipt::message_out_with_len(
sender,
recipient,
msg.amount,
nonce,
msg.len,
digest,
msg.data.clone(),
))
}
ProtoReceiptVariant::Mint(mint) => {
let sub_id = SubAssetId::try_from(mint.sub_id.as_slice())
.map_err(|e| Error::Serialization(anyhow!(e)))?;
let contract_id = ContractId::try_from(mint.contract_id.as_slice())View on GitHub (pinned to b9d4d170da)
Solutions
- Log the digest length of the failing MessageOut receipt.
- Pre-validate len == 32 for MessageOut digest before decoding.
- Align fuel-core versions on both ends.
- Reject and re-fetch the block.
Example fix
// before
let digest = Bytes32::try_from(msg.digest.as_slice()).map_err(|e| {
Error::Serialization(anyhow!("Could not convert message digest to Bytes32: {}", e))
})?;
// after
if msg.digest.len() != 32 {
return Err(Error::Serialization(anyhow::anyhow!(
"MessageOut digest length {} != 32", msg.digest.len()
)));
} Defensive patterns
Strategy: validation
Validate before calling
fn message_out_digest_valid(msg: &ProtoMessageOut) -> bool {
msg.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("message digest") => {
tracing::warn!(len = msg.digest.len(), "rejecting MessageOut with bad digest");
return Err(Error::Serialization(ctx));
}
Err(e) => return Err(e),
} Prevention
- Validate MessageOut digest length (32) together with the other digest fields.
- Use canonical fuel-core encoders; version-pin peers.
- Checksum payloads to detect corruption before decode.
When it happens
Trigger: fuel_block_from_protobuf on a block where a MessageOut receipt's digest is empty or not 32 bytes - corrupted blob, truncated frame, or variable-length encoding by the producer.
Common situations: Producer/consumer version mismatch; corruption in transit or storage; test fixtures with arbitrary digests.
Related errors
- Could not convert return data digest to Bytes32: {}
- Could not convert log data digest to Bytes32: {}
- Could not convert storage slot key to Bytes32: {}
- Could not convert storage slot value to Bytes32: {}
- Could not convert balance_root to Bytes32: {}
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/ac5d3e5b3ae8a01b.
Report an issue: GitHub.