FuelLabs/fuel-core · error · Error::Serialization
Missing tx_pointer on contract input
Error message
Missing tx_pointer on contract input
What it means
contract_input_from_proto requires the tx_pointer sub-message on every ProtoContractInput; prost yields None for absent optional message fields and this code converts that into Error::Serialization. fuel-core's Contract input type always carries a TxPointer, so an input without one cannot be represented and the whole block decode fails.
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:120
})?;
Ok(StorageSlot::new(key, value))
}
fn contract_input_from_proto(
proto: &ProtoContractInput,
) -> crate::result::Result<fuel_core_types::fuel_tx::input::contract::Contract> {
let utxo_proto = proto.utxo_id.as_ref().ok_or_else(|| {
Error::Serialization(anyhow!("Missing utxo_id on contract input"))
})?;
let utxo_id = utxo_id_from_proto(utxo_proto)?;
let balance_root = Bytes32::try_from(proto.balance_root.as_slice()).map_err(|e| {
Error::Serialization(anyhow!("Could not convert balance_root to Bytes32: {}", e))
})?;
let state_root = Bytes32::try_from(proto.state_root.as_slice()).map_err(|e| {
Error::Serialization(anyhow!("Could not convert state_root to Bytes32: {}", e))
})?;
let tx_pointer_proto = proto.tx_pointer.as_ref().ok_or_else(|| {
Error::Serialization(anyhow!("Missing tx_pointer on contract input"))
})?;
let tx_pointer = tx_pointer_from_proto(tx_pointer_proto)?;
let contract_id =
fuel_core_types::fuel_types::ContractId::try_from(proto.contract_id.as_slice())
.map_err(|e| Error::Serialization(anyhow!(e)))?;
Ok(fuel_core_types::fuel_tx::input::contract::Contract {
utxo_id,
balance_root,
state_root,
tx_pointer,
contract_id,
})
}
fn contract_output_from_proto(
proto: &ProtoContractOutput,
) -> crate::result::Result<fuel_core_types::fuel_tx::output::contract::Contract> {View on GitHub (pinned to b9d4d170da)
Solutions
- Debug-print the failing ProtoContractInput to identify missing sub-messages.
- Fix the encoder to always populate tx_pointer (and utxo_id) for contract inputs.
- Pre-validate presence of both sub-messages and reject the tx early with an index-annotated error.
- Re-obtain the block from a compatible peer.
Example fix
// before
let tx_pointer_proto = proto.tx_pointer.as_ref().ok_or_else(|| {
Error::Serialization(anyhow!("Missing tx_pointer on contract input"))
})?;
// after: validate whole input first
fn contract_input_complete(p: &ProtoContractInput) -> bool {
p.utxo_id.is_some() && p.tx_pointer.is_some()
}
if !contract_input_complete(input_proto) {
return Err(Error::Serialization(anyhow::anyhow!(
"input[{i}] incomplete contract input"
)));
} Defensive patterns
Strategy: type-guard
Validate before calling
fn contract_input_has_tx_pointer(p: &ProtoContractInput) -> bool {
p.tx_pointer.is_some()
} Type guard
fn contract_input_complete(p: &ProtoContractInput) -> bool {
p.utxo_id.is_some() && p.tx_pointer.is_some()
} Try / catch
match contract_input_from_proto(p) {
Ok(input) => Some(input),
Err(Error::Serialization(ctx)) if ctx.to_string().contains("tx_pointer") => {
tracing::warn!(?p, "rejecting contract input without tx_pointer");
None
}
Err(e) => return Err(e),
} Prevention
- Require full sub-message presence on contract inputs before conversion.
- Fix encoders to mirror fuel-core's Contract input (always sets UtxoId and TxPointer).
- Test decode paths with intentionally-emptied sub-messages so failures are positional and logged.
When it happens
Trigger: Decoding a proto transaction whose contract input omitted the tx_pointer field - produced by an older/incompatible encoder, a third-party producer, or fixtures that fill only some sub-messages.
Common situations: Version skew across fuel-core releases that changed which sub-messages are populated; hand-written proto in tests; migration tooling that drops fields it does not understand.
Related errors
- Missing utxo_id on contract input
- Could not convert balance_root to Bytes32: {}
- Could not convert state_root to Bytes32: {}
- Missing receipt variant
- Missing panic reason
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/048576257ec95826.
Report an issue: GitHub.