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

  1. Log the raw proto value (proto.tx_index) for the failing TxPointer to confirm it exceeds u16::MAX.
  2. Pin producer and consumer to matching fuel-core versions so tx_index is always serialized within the u16 range fuel_tx::TxPointer uses.
  3. If you own the producer, bound or regenerate the field to 0..=65535 before re-encoding.
  4. 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

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


AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16). Data as JSON: /api/errors/44a86149214fa7aa. Report an issue: GitHub.