FuelLabs/fuel-core · error · Error::Serialization
Could not convert tx_index to target type: {}
Error message
Could not convert tx_index to target type: {} What it means
Raised in tx_pointer_from_proto when converting a protobuf TxPointer into fuel_tx::TxPointer: the proto tx_index integer (a wide uint) is try_into-converted to the u16 transaction index that TxPointer::new requires, and the conversion fails when the value does not fit u16 (max 65535). It is wrapped as Error::Serialization and propagates out of fuel_block_from_protobuf / tx_from_proto_tx whenever a TxPointer is decoded (contract inputs, mint transactions).
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:83
Policies as FuelPolicies,
PoliciesBits,
PolicyType,
},
},
fuel_types::{
AssetId,
ContractId,
Nonce,
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
))View on GitHub (pinned to b9d4d170da)
Solutions
- Log the raw proto value (proto.tx_index) for the failing TxPointer to confirm it exceeds u16::MAX.
- Pin producer and consumer to matching fuel-core versions so tx_index is always serialized within the u16 range fuel_tx::TxPointer uses.
- If you own the producer, bound or regenerate the field to 0..=65535 before re-encoding.
- Reject the whole proto block and re-fetch it from a compatible peer rather than partially decoding it.
Example fix
// before: blind conversion
let tx_pointer = tx_pointer_from_proto(tx_pointer_proto)?;
// after: validate range at the boundary, reject with context
let tx_index: u16 = tx_pointer_proto.tx_index.try_into().map_err(|_| {
Error::Serialization(anyhow::anyhow!(
"tx_index {} out of range for TxPointer (max 65535)",
tx_pointer_proto.tx_index
))
})?;
let tx_pointer = TxPointer::new(tx_pointer_proto.block_height.into(), tx_index); Defensive patterns
Strategy: validation
Validate before calling
fn tx_pointer_valid(p: &ProtoTxPointer) -> bool {
p.tx_index <= u16::MAX as u32 // adjust to the proto integer width in your generated types
}
// gate the decode
if !block_tx_pointers(&proto_block).iter().all(|p| tx_pointer_valid(p)) {
return Err(Error::Serialization(anyhow::anyhow!("tx_index out of u16 range")));
} Type guard
fn tx_pointer_in_range(p: &ProtoTxPointer) -> bool {
(p.tx_index as u64) <= u16::MAX as u64
} Try / catch
match tx_pointer_from_proto(p) {
Ok(ptr) => Some(ptr),
Err(Error::Serialization(ctx)) if ctx.to_string().contains("tx_index") => {
tracing::warn!(index = p.tx_index, "rejecting TxPointer out of range");
None // drop the field or the whole tx per protocol
}
Err(e) => return Err(e),
} Prevention
- Only accept proto blocks from peers running a compatible fuel-core version (negotiate version in the handshake).
- Generate test data with the same bounds as fuel-core types (u16 for tx_index).
- Keep the roundtrip test (serialize_block__roundtrip) in CI to catch bound drift between proto and fuel types.
When it happens
Trigger: Decoding a proto block whose ProtoTxPointer.tx_index exceeds 65535 - e.g. produced by an encoder that stores tx_index as u32/u64 without fuel-core's u16 bound, a hand-crafted message, or a fuzz corpus with unconstrained integers.
Common situations: Version skew between the node that encoded the proto block and the node decoding it; third-party tools emitting the protobuf format without fuel-core type bounds; test fixtures generated by property testing that do not clamp tx_index.
Related errors
- Could not convert bytecode_witness_index to u16: {}
- Could not convert storage slot key to Bytes32: {}
- Could not convert storage slot value to Bytes32: {}
- Missing utxo_id on contract input
- Could not convert balance_root to Bytes32: {}
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/44a86149214fa7aa.
Report an issue: GitHub.