{"record":{"id":"fc7bc82955e2c0fb","repo":"FuelLabs/fuel-core","slug":"could-not-convert-receipts-root-to-bytes32","errorCode":null,"errorMessage":"Could not convert receipts_root to Bytes32: {}","messagePattern":"Could not convert receipts_root to Bytes32: (.+?)","errorType":"exception","errorClass":"Error::Serialization","httpStatus":null,"severity":"error","filePath":"crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs","lineNumber":613,"sourceCode":"            let witnesses = proto_script\n                .witnesses\n                .iter()\n                .map(|w| Ok(Witness::from(w.clone())))\n                .collect::<crate::result::Result<Vec<_>>>()?;\n            let mut script_tx = FuelTransaction::script(\n                proto_script.script_gas_limit,\n                proto_script.script.clone(),\n                proto_script.script_data.clone(),\n                policies,\n                inputs,\n                outputs,\n                witnesses,\n            );\n            *script_tx.receipts_root_mut() = Bytes32::try_from(\n                proto_script.receipts_root.as_slice(),\n            )\n            .map_err(|e| {\n                Error::Serialization(anyhow!(\n                    \"Could not convert receipts_root to Bytes32: {}\",\n                    e\n                ))\n            })?;\n\n            Ok(FuelTransaction::Script(script_tx))\n        }\n        ProtoTransactionVariant::Create(proto_create) => {\n            let policies = proto_create\n                .policies\n                .clone()\n                .map(|p| policies_from_proto_policies(&p))\n                .unwrap_or_default();\n            let inputs = proto_create\n                .inputs\n                .iter()\n                .map(input_from_proto_input)\n                .collect::<crate::result::Result<Vec<_>>>()?;","sourceCodeStart":595,"sourceCodeEnd":631,"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#L595-L631","documentation":"While decoding a Script transaction, the receipts_root bytes field must convert to Bytes32, which requires exactly 32 bytes; any other length fails try_from and is wrapped as Error::Serialization, failing the transaction and block. Note the setter path: the decoded Script tx is built first and receipts_root_mut() is overwritten from the proto field.","triggerScenarios":"fuel_block_from_protobuf on a block where a Script transaction's receipts_root is empty or not 32 bytes - corruption, truncation, or a producer that does not fix the field size.","commonSituations":"Producer/consumer version mismatch; corrupted storage or transfer; test fixtures with default empty byte fields.","solutions":["Log the receipts_root length of the failing Script tx.","Pre-validate len == 32 before decoding the block.","Pin matching fuel-core versions on both ends so receipts_root is always 32 bytes.","Re-fetch the block from a known-good peer."],"exampleFix":"// before\n*script_tx.receipts_root_mut() =\n    Bytes32::try_from(proto_script.receipts_root.as_slice()).map_err(|e| {\n        Error::Serialization(anyhow!(\"Could not convert receipts_root to Bytes32: {}\", e))\n    })?;\n\n// after: caller-side pre-check\nif proto_script.receipts_root.len() != 32 {\n    return Err(Error::Serialization(anyhow::anyhow!(\n        \"receipts_root length {} != 32\", proto_script.receipts_root.len()\n    )));\n}","handlingStrategy":"validation","validationCode":"fn script_receipts_root_valid(script: &ProtoScript) -> bool {\n    script.receipts_root.len() == 32\n}","typeGuard":"fn is_bytes32(b: &[u8]) -> bool { b.len() == 32 }","tryCatchPattern":"match tx_from_proto_tx(t) {\n    Ok(tx) => txs.push(tx),\n    Err(Error::Serialization(ctx)) if ctx.to_string().contains(\"receipts_root\") => {\n        tracing::warn!(len = script.receipts_root.len(), \"rejecting Script tx with bad receipts_root\");\n        return Err(Error::Serialization(ctx));\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate receipts_root length (32) on Script transactions before block decode.","Pin compatible fuel-core versions so roots are always fixed-size.","Re-encode from canonical on-chain data instead of repairing proto bytes."],"tags":["rust","fuel-core","protobuf","deserialization","bytes32","transactions"],"backgroundTag":null,"analyzedSha":"b9d4d170da3a31c9ace5f963d633b326348e0d42","analyzedAt":"2026-08-16T08:56:42.692Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}