{"record":{"id":"63aa36a3f76b49c3","repo":"FuelLabs/fuel-core","slug":"could-not-convert-balance-root-to-bytes32","errorCode":null,"errorMessage":"Could not convert balance_root to Bytes32: {}","messagePattern":"Could not convert balance_root to Bytes32: (.+?)","errorType":"validation","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":114,"sourceCode":"    })?;\n    let value = Bytes32::try_from(proto.value.as_slice()).map_err(|e| {\n        Error::Serialization(anyhow!(\n            \"Could not convert storage slot value to Bytes32: {}\",\n            e\n        ))\n    })?;\n    Ok(StorageSlot::new(key, value))\n}\n\nfn contract_input_from_proto(\n    proto: &ProtoContractInput,\n) -> crate::result::Result<fuel_core_types::fuel_tx::input::contract::Contract> {\n    let utxo_proto = proto.utxo_id.as_ref().ok_or_else(|| {\n        Error::Serialization(anyhow!(\"Missing utxo_id on contract input\"))\n    })?;\n    let utxo_id = utxo_id_from_proto(utxo_proto)?;\n    let balance_root = Bytes32::try_from(proto.balance_root.as_slice()).map_err(|e| {\n        Error::Serialization(anyhow!(\"Could not convert balance_root to Bytes32: {}\", e))\n    })?;\n    let state_root = Bytes32::try_from(proto.state_root.as_slice()).map_err(|e| {\n        Error::Serialization(anyhow!(\"Could not convert state_root to Bytes32: {}\", e))\n    })?;\n    let tx_pointer_proto = proto.tx_pointer.as_ref().ok_or_else(|| {\n        Error::Serialization(anyhow!(\"Missing tx_pointer on contract input\"))\n    })?;\n    let tx_pointer = tx_pointer_from_proto(tx_pointer_proto)?;\n    let contract_id =\n        fuel_core_types::fuel_types::ContractId::try_from(proto.contract_id.as_slice())\n            .map_err(|e| Error::Serialization(anyhow!(e)))?;\n\n    Ok(fuel_core_types::fuel_tx::input::contract::Contract {\n        utxo_id,\n        balance_root,\n        state_root,\n        tx_pointer,\n        contract_id,","sourceCodeStart":96,"sourceCodeEnd":132,"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#L96-L132","documentation":"Thrown while decoding a proto contract input: the balance_root bytes field must convert to Bytes32, which requires exactly 32 bytes; any other length fails the try_from and is wrapped as Error::Serialization. The error aborts the contract input, its transaction, and the block decode.","triggerScenarios":"fuel_block_from_protobuf on a block where a ProtoContractInput.balance_root has a length other than 32 - empty field, truncated data, or a producer that serializes unbounded bytes.","commonSituations":"Incompatible fuel-core versions on producer/consumer; corrupted transfer or storage; hand-built test messages with default empty Vec fields.","solutions":["Log the balance_root length for the failing input to confirm the mismatch.","Validate len == 32 for balance_root and state_root on every contract input before decoding.","Pin both ends to the same fuel-core/protobuf schema version.","Reject and re-fetch the block from a compatible peer."],"exampleFix":"// before\nlet balance_root = Bytes32::try_from(proto.balance_root.as_slice()).map_err(|e| {\n    Error::Serialization(anyhow!(\"Could not convert balance_root to Bytes32: {}\", e))\n})?;\n\n// after: caller pre-check\nif input_proto.balance_root.len() != 32 {\n    return Err(Error::Serialization(anyhow::anyhow!(\n        \"balance_root length {} != 32\", input_proto.balance_root.len()\n    )));\n}","handlingStrategy":"validation","validationCode":"fn contract_input_roots_valid(p: &ProtoContractInput) -> bool {\n    p.balance_root.len() == 32 && p.state_root.len() == 32\n}","typeGuard":"fn is_bytes32(b: &[u8]) -> bool { b.len() == 32 }","tryCatchPattern":"match contract_input_from_proto(p) {\n    Ok(input) => inputs.push(input),\n    Err(Error::Serialization(ctx)) if ctx.to_string().contains(\"balance_root\") => {\n        tracing::warn!(len = p.balance_root.len(), \"rejecting input with bad balance_root\");\n        return Err(Error::Serialization(ctx));\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate 32-byte fields on every contract input before decoding a block.","Keep producer and consumer fuel-core versions aligned so roots are always fixed-size.","Never hand-construct these proto messages without fixed-size byte arrays."],"tags":["rust","fuel-core","protobuf","deserialization","bytes32","contract-input"],"backgroundTag":null,"analyzedSha":"b9d4d170da3a31c9ace5f963d633b326348e0d42","analyzedAt":"2026-08-16T08:56:42.692Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}