{"record":{"id":"e63af4fb7bcd56ad","repo":"FuelLabs/fuel-core","slug":"could-not-convert-return-data-digest-to-bytes32","errorCode":null,"errorMessage":"Could not convert return data digest to Bytes32: {}","messagePattern":"Could not convert return data digest to Bytes32: (.+?)","errorType":"exception","errorClass":"Error::Serialization","httpStatus":500,"severity":"error","filePath":"crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs","lineNumber":336,"sourceCode":"                call.amount,\n                asset_id,\n                call.gas,\n                call.param1,\n                call.param2,\n                call.pc,\n                call.is,\n            ))\n        }\n        ProtoReceiptVariant::ReturnReceipt(ret) => {\n            let id = ContractId::try_from(ret.id.as_slice())\n                .map_err(|e| Error::Serialization(anyhow!(e)))?;\n            Ok(FuelReceipt::ret(id, ret.val, ret.pc, ret.is))\n        }\n        ProtoReceiptVariant::ReturnData(rd) => {\n            let id = ContractId::try_from(rd.id.as_slice())\n                .map_err(|e| Error::Serialization(anyhow!(e)))?;\n            let digest = Bytes32::try_from(rd.digest.as_slice()).map_err(|e| {\n                Error::Serialization(anyhow!(\n                    \"Could not convert return data digest to Bytes32: {}\",\n                    e\n                ))\n            })?;\n            Ok(FuelReceipt::return_data_with_len(\n                id,\n                rd.ptr,\n                rd.len,\n                digest,\n                rd.pc,\n                rd.is,\n                rd.data.clone(),\n            ))\n        }\n        ProtoReceiptVariant::Panic(panic_receipt) => {\n            let id = ContractId::try_from(panic_receipt.id.as_slice())\n                .map_err(|e| Error::Serialization(anyhow!(e)))?;\n            let reason_proto = panic_receipt","sourceCodeStart":318,"sourceCodeEnd":354,"githubUrl":"https://github.com/FuelLabs/fuel-core/blob/b9d4d170da3a31c9ace5f963d633b326348e0d42/crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs#L318-L354","documentation":"Thrown while decoding a ReturnData receipt: the digest bytes field must be exactly 32 bytes to build fuel's Bytes32; any other length fails Bytes32::try_from and is wrapped as Error::Serialization, failing the receipt, its transaction, and the block.","triggerScenarios":"fuel_block_from_protobuf on a block where a ReturnData receipt's digest field is empty or not 32 bytes - corrupted blob, truncated frame, or an encoder that writes unbounded bytes for the digest.","commonSituations":"Version-incompatible producer; data corruption in transit/storage; test fixtures with arbitrary digest lengths.","solutions":["Log the digest length of the failing ReturnData receipt.","Pre-validate len == 32 for digest fields on ReturnData/LogData/MessageOut receipts before decoding.","Align producer and consumer versions so digests are always 32 bytes.","Re-fetch the block from a healthy source."],"exampleFix":"// before\nlet digest = Bytes32::try_from(rd.digest.as_slice()).map_err(|e| {\n    Error::Serialization(anyhow!(\"Could not convert return data digest to Bytes32: {}\", e))\n})?;\n\n// after\nif rd.digest.len() != 32 {\n    return Err(Error::Serialization(anyhow::anyhow!(\n        \"ReturnData digest length {} != 32\", rd.digest.len()\n    )));\n}","handlingStrategy":"validation","validationCode":"fn return_data_digest_valid(rd: &ProtoReturnData) -> bool {\n    rd.digest.len() == 32\n}","typeGuard":"fn is_bytes32(b: &[u8]) -> bool { b.len() == 32 }","tryCatchPattern":"match receipt_from_proto(r) {\n    Ok(receipt) => out.push(receipt),\n    Err(Error::Serialization(ctx)) if ctx.to_string().contains(\"return data digest\") => {\n        tracing::warn!(len = rd.digest.len(), \"rejecting ReturnData with bad digest\");\n        return Err(Error::Serialization(ctx));\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate all digest fields (ReturnData, LogData, MessageOut) are 32 bytes before decoding.","Pin producer/consumer versions so digests are always fixed-size.","Corruption in digests usually means transport or storage damage - checksum payloads."],"tags":["rust","fuel-core","protobuf","deserialization","bytes32","receipts"],"backgroundTag":null,"analyzedSha":"b9d4d170da3a31c9ace5f963d633b326348e0d42","analyzedAt":"2026-08-16T08:56:42.692Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}