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
- Validate sender.len() == 32 on message inputs before conversion
- Fix the message relay/bridge producer to serialize the full Fuel Address of the sender
- Where a 20-byte source address is legitimate, hash/pad it to 32 bytes explicitly at the boundary
- 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
- Bridge/relay code must always fill the 32-byte sender when minting message protos
- Validate sender and recipient together in one pass
- Add message-input fixtures to producer round-trip tests
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
- Could not convert recipient to Address: {}
- Could not convert owner to Address: {}
- Could not convert upload root to Bytes32: {}
- Could not convert witness_index to u16: {}
- Could not convert subsection_index to u16: {}
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/f0e4eeb274d86538.
Report an issue: GitHub.