FuelLabs/fuel-core · error · Error::Serialization
Missing tx_pointer
Error message
Missing tx_pointer
What it means
CoinSigned inputs embed a tx_pointer submessage used for dependency ordering; it is optional at the proto layer but required by Input::coin_signed. When tx_pointer is None the convertor fails with 'Missing tx_pointer' before tx_pointer_from_proto can run.
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:886
.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 =
u16::try_from(proto_coin_signed.witness_index).map_err(|e| {
Error::Serialization(anyhow!(
"Could not convert witness_index to u16: {}",
e
))
})?;
Ok(Input::coin_signed(
utxo_id,
owner,
proto_coin_signed.amount,
asset_id,
tx_pointer,
witness_index,
))
}View on GitHub (pinned to b9d4d170da)
Solutions
- Fix the producer to attach tx_pointer (block height + tx index) to every coin input
- Pre-validate inputs: CoinSigned requires utxo_id AND tx_pointer submessages
- Re-encode legacy data with a zeroed tx_pointer (TxPointer::new(0, 0)) if the chain accepts it, as an explicit migration step
- Pin matching proto/fuel-core versions across the pipeline
Example fix
// before
let tx_pointer_proto = proto_coin_signed.tx_pointer.as_ref().ok_or_else(|| Error::Serialization(anyhow!("Missing tx_pointer")))?;
// after (migration shim for legacy data)
let tx_pointer = proto_coin_signed
.tx_pointer
.as_ref()
.map(tx_pointer_from_proto)
.transpose()?
.unwrap_or(fuel_core_types::fuel_tx::TxPointer::new(0, 0)); 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_tx_pointer(c: &ProtoCoinSigned) -> bool { c.tx_pointer.is_some() } Try / catch
let tp = c.tx_pointer.as_ref().ok_or_else(|| anyhow!("CoinSigned utxo {:?} missing tx_pointer", c.utxo_id))?; Prevention
- Treat tx_pointer as required metadata for every coin input variant
- Shared completeness check per variant: signed and predicate coins need the same submessages
- Re-encode historical data with explicit defaults via a reviewed migration
When it happens
Trigger: A CoinSigned input proto where the nested tx_pointer message was omitted — partial encoder output, legacy schema, or default-constructed structs in tests.
Common situations: Producers that treat tx_pointer as optional metadata; schema drift; blocks encoded before tx_pointer was mandatory for coin inputs.
Related errors
- Missing utxo_id
- 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/89bc0bc452af6b88.
Report an issue: GitHub.