FuelLabs/fuel-core · error · Error::Serialization
Could not convert storage slot key to Bytes32: {}
Error message
Could not convert storage slot key to Bytes32: {} What it means
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.
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:92
SubAssetId,
},
tai64,
};
fn tx_pointer_from_proto(proto: &ProtoTxPointer) -> crate::result::Result<TxPointer> {
let block_height = proto.block_height.into();
#[allow(clippy::useless_conversion)]
let tx_index = proto.tx_index.try_into().map_err(|e| {
Error::Serialization(anyhow!("Could not convert tx_index to target type: {}", e))
})?;
Ok(TxPointer::new(block_height, tx_index))
}
fn storage_slot_from_proto(
proto: &ProtoStorageSlot,
) -> crate::result::Result<StorageSlot> {
let key = Bytes32::try_from(proto.key.as_slice()).map_err(|e| {
Error::Serialization(anyhow!(
"Could not convert storage slot key to Bytes32: {}",
e
))
})?;
let value = Bytes32::try_from(proto.value.as_slice()).map_err(|e| {
Error::Serialization(anyhow!(
"Could not convert storage slot value to Bytes32: {}",
e
))
})?;
Ok(StorageSlot::new(key, value))
}
fn contract_input_from_proto(
proto: &ProtoContractInput,
) -> crate::result::Result<fuel_core_types::fuel_tx::input::contract::Contract> {
let utxo_proto = proto.utxo_id.as_ref().ok_or_else(|| {
Error::Serialization(anyhow!("Missing utxo_id on contract input"))View on GitHub (pinned to b9d4d170da)
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.
Example fix
// before: convert directly and fail deep inside decoding
let storage_slots: Vec<StorageSlot> = proto_create.storage_slots.iter()
.map(storage_slot_from_proto).collect::<Result<_>>()?;
// after: validate first, fail fast with slot position
for (i, slot) in proto_create.storage_slots.iter().enumerate() {
if slot.key.len() != 32 || slot.value.len() != 32 {
return Err(Error::Serialization(anyhow::anyhow!(
"storage slot {} has key.len={} value.len={}; expected 32/32",
i, slot.key.len(), slot.value.len()
)));
}
} Defensive patterns
Strategy: validation
Validate before calling
fn storage_slots_valid(create: &ProtoCreate) -> bool {
create.storage_slots.iter().all(|s| s.key.len() == 32 && s.value.len() == 32)
} Type guard
fn is_bytes32(b: &[u8]) -> bool { b.len() == 32 } Try / catch
match storage_slot_from_proto(slot) {
Ok(s) => slots.push(s),
Err(Error::Serialization(ctx)) if ctx.to_string().contains("storage slot") => {
tracing::warn!(key_len = slot.key.len(), "rejecting malformed storage slot");
return Err(Error::Serialization(ctx)); // or skip tx per policy
}
Err(e) => return Err(e),
} Prevention
- 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>.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Could not convert storage slot value to Bytes32: {}
- Could not convert balance_root to Bytes32: {}
- Could not convert state_root to Bytes32: {}
- Could not convert return data digest to Bytes32: {}
- Could not convert log data digest to Bytes32: {}
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/cd2f068a7e30aadb.
Report an issue: GitHub.