FuelLabs/fuel-core · error · Error::Serialization
Could not convert recipient to Address: {}
Error message
Could not convert recipient to Address: {} What it means
The companion check to error 197: MessageCoinSigned recipient bytes must convert to a 32-byte Address. Address::try_from on proto_message.recipient fails for any length other than 32 and is reported as 'Could not convert recipient to Address: {e}'.
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:953
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!(
"Could not convert witness_index to u16: {}",
e
))
})?;
Ok(Input::message_coin_signed(
sender,View on GitHub (pinned to b9d4d170da)
Solutions
- Validate both sender and recipient are 32 bytes before converting message inputs
- Fix the producer to always set the recipient Address on message coins
- Reject unspendable messages (empty recipient) at ingestion with a targeted message instead of a generic conversion error
- Add round-trip serialization tests for message inputs in the producer
Example fix
// before
let recipient = Address::try_from(proto_message.recipient.as_slice()).map_err(...)?;
// after
for (name, b) in [("sender", &proto_message.sender), ("recipient", &proto_message.recipient)] {
if b.len() != 32 { return Err(anyhow!("message {name} len {} != 32", b.len())); }
}
let recipient = Address::try_from(proto_message.recipient.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 && m.nonce.len() == 32
} Type guard
fn is_fuel_address(b: &[u8]) -> bool { b.len() == 32 } Try / catch
let recipient = Address::try_from(m.recipient.as_slice())
.with_context(|| format!("message recipient len {} (expected 32)", m.recipient.len()))?; Prevention
- Reject messages with empty recipients at ingestion — they are unspendable
- Validate all fixed-width fields (sender, recipient, nonce) in a single pre-pass
- Round-trip test message inputs end to end
When it happens
Trigger: A message-coin input whose recipient is unset (0 bytes) or a non-32-byte value; sender may have passed just before this check fails.
Common situations: Bridge/relay producers that drop recipient on failure paths; address-format mismatches from other ecosystems; truncated payloads; incomplete test messages.
Related errors
- Could not convert sender 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/ab1ecdc73fbc7750.
Report an issue: GitHub.