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

  1. Fix the producer to set utxo_id (tx_id + output_index) on every coin input it serializes
  2. Pre-validate coin inputs: variant-specific required submessages (utxo_id, tx_pointer) must be Some
  3. Align proto versions between the block source and this crate
  4. 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

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


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