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

Missing input variant

Error message

Missing input variant

What it means

ProtoInput models inputs as a oneof variant; when the variant field itself is None there is no way to tell coin/message/contract apart, so input_from_proto_input fails fast with this serialization error before any field checks.

Source

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

            })?;
            let body = BlobBody {
                id: blob_id,
                witness_index,
            };

            let blob_tx =
                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(),
            )

View on GitHub (pinned to b9d4d170da)

Solutions

  1. Fix the producer to always populate the input variant oneof when appending inputs
  2. Pre-scan inputs before conversion and reject transactions with variant-less ProtoInput entries, logging tx id and input position
  3. Align proto definitions between producer and this crate
  4. For legacy data, re-encode inputs with an explicit variant chosen from the transaction type context

Example fix

// before
let variant = proto_input.variant.as_ref().ok_or_else(|| Error::Serialization(anyhow!("Missing input variant")))?;

// after
let Some(variant) = proto_input.variant.as_ref() else {
    // skip-and-report keeps one malformed input from killing the whole block ingest
    report_bad_input(&proto_input);
    continue;
};
Defensive patterns

Strategy: type-guard

Validate before calling

fn all_inputs_have_variants(tx: &ProtoTransaction) -> bool {
    tx.inputs.iter().all(|i| i.variant.is_some())
}

Type guard

fn input_variant_present(i: &ProtoInput) -> bool { i.variant.is_some() }

Try / catch

let variant = i.variant.as_ref().ok_or_else(|| {
    anyhow!("input #{idx} in tx {:?} has no variant — producer bug", tx_id)
})?;

Prevention

When it happens

Trigger: A transaction input proto with the variant oneof never set — an encoder produced a bare ProtoInput, or the payload was truncated after the outer message. Any transaction type containing inputs (script, upload, blob, upgrade, mint) can surface this.

Common situations: Default-constructed ProtoInput structs (prost makes all-oneof-None valid); partial serialization from a buggy client; schema-skew where the input oneof was renamed.

Related errors


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