FuelLabs/fuel-core · error · Error::Serialization

Could not convert sender to Address: {}

Error message

Could not convert sender to Address: {}

What it means

MessageCoinSigned inputs carry a 32-byte sender address; the convertor runs Address::try_from on proto_message.sender and wraps any failure as 'Could not convert sender to Address: {e}'. The try_from fails when the byte slice is not exactly 32 bytes, most commonly 0 (field never set).

Source

Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:946

            Ok(Input::coin_predicate(
                utxo_id,
                owner,
                proto_coin_predicate.amount,
                asset_id,
                tx_pointer,
                proto_coin_predicate.predicate_gas_used,
                proto_coin_predicate.predicate.clone(),
                proto_coin_predicate.predicate_data.clone(),
            ))
        }
        ProtoInputVariant::Contract(proto_contract) => {
            let contract = contract_input_from_proto(proto_contract)?;
            Ok(Input::Contract(contract))
        }
        ProtoInputVariant::MessageCoinSigned(proto_message) => {
            let sender =
                Address::try_from(proto_message.sender.as_slice()).map_err(|e| {
                    Error::Serialization(anyhow!(
                        "Could not convert sender to Address: {}",
                        e
                    ))
                })?;
            let recipient = Address::try_from(proto_message.recipient.as_slice())
                .map_err(|e| {
                    Error::Serialization(anyhow!(
                        "Could not convert recipient to Address: {}",
                        e
                    ))
                })?;
            let nonce = fuel_core_types::fuel_types::Nonce::try_from(
                proto_message.nonce.as_slice(),
            )
            .map_err(|e| Error::Serialization(anyhow!(e)))?;
            let witness_index =
                u16::try_from(proto_message.witness_index).map_err(|e| {
                    Error::Serialization(anyhow!(

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Validate sender.len() == 32 on message inputs before conversion
  2. Fix the message relay/bridge producer to serialize the full Fuel Address of the sender
  3. Where a 20-byte source address is legitimate, hash/pad it to 32 bytes explicitly at the boundary
  4. Include input index and byte length in validation errors to locate the offending producer fast

Example fix

// before
let sender = Address::try_from(proto_message.sender.as_slice()).map_err(...)?;

// after
if proto_message.sender.len() != 32 {
    return Err(anyhow!("message sender len {} != 32", proto_message.sender.len()));
}
let sender = Address::try_from(proto_message.sender.as_slice()).map_err(...)?;
Defensive patterns

Strategy: validation

Validate before calling

fn message_input_is_valid(m: &ProtoMessageCoinSigned) -> bool {
    m.sender.len() == 32 && m.recipient.len() == 32
}

Type guard

fn is_fuel_address(b: &[u8]) -> bool { b.len() == 32 }

Try / catch

let sender = Address::try_from(m.sender.as_slice())
    .with_context(|| format!("message sender len {} (expected 32)", m.sender.len()))?;

Prevention

When it happens

Trigger: A message-coin input with sender unset or wrong width (20-byte address, truncated key). Triggered while decoding any transaction containing message inputs (script/mint with messages).

Common situations: Producer omitted sender when relaying messages; bridging tooling emitting L1-style 20-byte addresses; corrupted transport payloads; fixture messages with empty sender.

Related errors


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