FuelLabs/fuel-core · error · Error::Serialization
Missing utxo_id
Error message
Missing utxo_id
What it means
Inside the CoinSigned input variant, utxo_id is a nested proto message (not a plain field), so prost makes it optional. If it is None the convertor cannot build the UtxoId and raises 'Missing utxo_id' before constructing Input::coin_signed.
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:870
FuelTransaction::blob(body, policies, inputs, outputs, witnesses);
Ok(FuelTransaction::Blob(blob_tx))
}
}
}
fn input_from_proto_input(proto_input: &ProtoInput) -> crate::result::Result<Input> {
let variant = proto_input
.variant
.as_ref()
.ok_or_else(|| Error::Serialization(anyhow!("Missing input variant")))?;
match variant {
ProtoInputVariant::CoinSigned(proto_coin_signed) => {
let utxo_proto = proto_coin_signed
.utxo_id
.as_ref()
.ok_or_else(|| Error::Serialization(anyhow!("Missing utxo_id")))?;
let utxo_id = utxo_id_from_proto(utxo_proto)?;
let owner =
Address::try_from(proto_coin_signed.owner.as_slice()).map_err(|e| {
Error::Serialization(anyhow!(
"Could not convert owner to Address: {}",
e
))
})?;
let asset_id = fuel_core_types::fuel_types::AssetId::try_from(
proto_coin_signed.asset_id.as_slice(),
)
.map_err(|e| Error::Serialization(anyhow!(e)))?;
let tx_pointer_proto = proto_coin_signed
.tx_pointer
.as_ref()
.ok_or_else(|| Error::Serialization(anyhow!("Missing tx_pointer")))?;
let tx_pointer = tx_pointer_from_proto(tx_pointer_proto)?;
let witness_index =View on GitHub (pinned to b9d4d170da)
Solutions
- Fix the producer to set utxo_id (tx_id + output_index) on every coin input it serializes
- Pre-validate coin inputs: variant-specific required submessages (utxo_id, tx_pointer) must be Some
- Align proto versions between the block source and this crate
- Quarantine transactions with incomplete coin inputs and request re-encoding from source
Example fix
// before
let utxo_proto = proto_coin_signed.utxo_id.as_ref().ok_or_else(|| Error::Serialization(anyhow!("Missing utxo_id")))?;
// after (producer side)
let input = ProtoInput {
variant: Some(ProtoInputVariant::CoinSigned(ProtoCoinSigned {
utxo_id: Some(ProtoUtxoId { tx_id: tx_id_bytes, output_index: 0 }),
..Default::default()
})),
}; Defensive patterns
Strategy: validation
Validate before calling
fn coin_signed_is_complete(c: &ProtoCoinSigned) -> bool {
c.utxo_id.is_some() && c.tx_pointer.is_some()
} Type guard
fn has_utxo_id(c: &ProtoCoinSigned) -> bool { c.utxo_id.is_some() } Try / catch
let utxo = c.utxo_id.as_ref().ok_or_else(|| {
anyhow!("CoinSigned input (owner {}) missing utxo_id", hex::encode(&c.owner))
})?; Prevention
- Model required nested submessages as mandatory in producer-side builders
- Validate coin inputs (utxo_id + tx_pointer present) before encoding to proto
- Keep conversion pre-checks in one helper reused across coin variants
When it happens
Trigger: A CoinSigned input proto where the utxo_id submessage was never attached — partial encoders, version skew where utxo_id changed shape, or hand-built test inputs.
Common situations: Default-constructed input structs; a producer that fills owner/amount but forgets the nested utxo_id; importing blocks encoded by an older client.
Related errors
- Missing tx_pointer
- Missing input_contract on mint transaction
- Missing output_contract on mint transaction
- Missing purpose on upgrade transaction
- Missing input variant
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/085ea1eba6df6870.
Report an issue: GitHub.