{"record":{"id":"cd2f068a7e30aadb","repo":"FuelLabs/fuel-core","slug":"could-not-convert-storage-slot-key-to-bytes32","errorCode":null,"errorMessage":"Could not convert storage slot key to Bytes32: {}","messagePattern":"Could not convert storage slot key 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":92,"sourceCode":"        SubAssetId,\n    },\n    tai64,\n};\n\nfn tx_pointer_from_proto(proto: &ProtoTxPointer) -> crate::result::Result<TxPointer> {\n    let block_height = proto.block_height.into();\n    #[allow(clippy::useless_conversion)]\n    let tx_index = proto.tx_index.try_into().map_err(|e| {\n        Error::Serialization(anyhow!(\"Could not convert tx_index to target type: {}\", e))\n    })?;\n    Ok(TxPointer::new(block_height, tx_index))\n}\n\nfn storage_slot_from_proto(\n    proto: &ProtoStorageSlot,\n) -> crate::result::Result<StorageSlot> {\n    let key = Bytes32::try_from(proto.key.as_slice()).map_err(|e| {\n        Error::Serialization(anyhow!(\n            \"Could not convert storage slot key to Bytes32: {}\",\n            e\n        ))\n    })?;\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\"))","sourceCodeStart":74,"sourceCodeEnd":110,"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#L74-L110","documentation":"Thrown by storage_slot_from_proto when converting a proto Create-transaction storage slot: Bytes32::try_from(&[u8]) accepts exactly 32 bytes, and the proto key field has a different length. Wrapped as Error::Serialization, it aborts conversion of the storage slot, then the containing Create transaction and the whole block.","triggerScenarios":"fuel_block_from_protobuf on a block where some ProtoStorageSlot.key is empty or any length other than 32 - typically corrupted blobs, truncated network frames, or a producer that writes unbounded bytes fields instead of fixed 32-byte keys.","commonSituations":"Proto produced by an incompatible fuel-core version or a hand-built serializer; data corrupted in transit or on disk; tests using arbitrary byte vectors instead of fixed 32-byte keys.","solutions":["Log the actual key length of the failing slot to confirm the mismatch.","Validate all fixed-size byte fields (len == 32) at the trust boundary before calling fuel_block_from_protobuf.","Ensure producer and consumer run the same fuel-core version so Bytes32 fields are always serialized as exactly 32 bytes.","Re-fetch or re-encode the block from a known-good source; do not pad or truncate unless you own the data end to end."],"exampleFix":"// before: convert directly and fail deep inside decoding\nlet storage_slots: Vec<StorageSlot> = proto_create.storage_slots.iter()\n    .map(storage_slot_from_proto).collect::<Result<_>>()?;\n\n// after: validate first, fail fast with slot position\nfor (i, slot) in proto_create.storage_slots.iter().enumerate() {\n    if slot.key.len() != 32 || slot.value.len() != 32 {\n        return Err(Error::Serialization(anyhow::anyhow!(\n            \"storage slot {} has key.len={} value.len={}; expected 32/32\",\n            i, slot.key.len(), slot.value.len()\n        )));\n    }\n}","handlingStrategy":"validation","validationCode":"fn storage_slots_valid(create: &ProtoCreate) -> bool {\n    create.storage_slots.iter().all(|s| s.key.len() == 32 && s.value.len() == 32)\n}","typeGuard":"fn is_bytes32(b: &[u8]) -> bool { b.len() == 32 }","tryCatchPattern":"match storage_slot_from_proto(slot) {\n    Ok(s) => slots.push(s),\n    Err(Error::Serialization(ctx)) if ctx.to_string().contains(\"storage slot\") => {\n        tracing::warn!(key_len = slot.key.len(), \"rejecting malformed storage slot\");\n        return Err(Error::Serialization(ctx)); // or skip tx per policy\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Validate all fixed-size (32-byte) fields at the trust boundary before fuel_block_from_protobuf.","Pin matching fuel-core versions on encoder and decoder.","Add checksums or length prefixes when persisting/transferring encoded blocks.","In tests, build keys/values with Bytes32::from(<[u8; 32]>) helpers rather than arbitrary Vec<u8>."],"tags":["rust","fuel-core","protobuf","deserialization","bytes32","storage-slot"],"backgroundTag":null,"analyzedSha":"b9d4d170da3a31c9ace5f963d633b326348e0d42","analyzedAt":"2026-08-16T08:56:42.692Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}