FuelLabs/fuel-core · error · Error::Serialization
Missing output_contract on mint transaction
Error message
Missing output_contract on mint transaction
What it means
The Mint transaction conversion needs both a contract input and a contract output; this error fires when the proto mint message has output_contract unset. It is a hard failure (Error::Serialization) raised before FuelTransaction::mint is constructed, because the fuel-core Mint type requires the output contract.
Source
Thrown at crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs:684
);
Ok(FuelTransaction::Create(create_tx))
}
ProtoTransactionVariant::Mint(proto_mint) => {
let tx_pointer_proto = proto_mint.tx_pointer.as_ref().ok_or_else(|| {
Error::Serialization(anyhow!("Missing tx_pointer on mint transaction"))
})?;
let tx_pointer = tx_pointer_from_proto(tx_pointer_proto)?;
let input_contract_proto =
proto_mint.input_contract.as_ref().ok_or_else(|| {
Error::Serialization(anyhow!(
"Missing input_contract on mint transaction"
))
})?;
let input_contract = contract_input_from_proto(input_contract_proto)?;
let output_contract_proto =
proto_mint.output_contract.as_ref().ok_or_else(|| {
Error::Serialization(anyhow!(
"Missing output_contract on mint transaction"
))
})?;
let output_contract = contract_output_from_proto(output_contract_proto)?;
let mint_asset_id = fuel_core_types::fuel_types::AssetId::try_from(
proto_mint.mint_asset_id.as_slice(),
)
.map_err(|e| Error::Serialization(anyhow!(e)))?;
let mint_tx = FuelTransaction::mint(
tx_pointer,
input_contract,
output_contract,
proto_mint.mint_amount,
mint_asset_id,
proto_mint.gas_price,
);
View on GitHub (pinned to b9d4d170da)
Solutions
- Fix the producer to always set output_contract on mint transactions and re-encode the affected blocks
- Align proto definitions (protobuf_types.rs) between producer and consumer versions
- Validate mint messages up front and quarantine blocks containing incomplete mint transactions instead of failing the whole stream
- Re-encode legacy data through a migration that derives the output contract from the input contract's contract_id
Example fix
// before
if let Some(mint) = variant_as_mint(&proto_tx) { convert_mint(mint)?; } // fails mid-way
// after
fn mint_is_complete(m: &ProtoMint) -> bool {
m.tx_pointer.is_some() && m.input_contract.is_some() && m.output_contract.is_some()
}
if mint_is_complete(&mint) { convert_mint(&mint)?; } else { skip_and_report(&proto_tx); } Defensive patterns
Strategy: validation
Validate before calling
fn mint_tx_is_complete(m: &ProtoMint) -> bool {
m.tx_pointer.is_some() && m.input_contract.is_some() && m.output_contract.is_some()
} Type guard
fn has_output_contract(m: &ProtoMint) -> bool { m.output_contract.is_some() } Try / catch
if let Err(e) = convert_transaction(&proto_tx) {
if e.to_string().contains("Missing output_contract on mint") {
metrics::incomplete_mint_tx.inc(); quarantine(proto_tx.id());
} else { return Err(e); }
} Prevention
- Treat mint's input_contract and output_contract as required in producer code reviews
- Add schema-level comments marking the fields as mandatory for round-tripping
- Re-encode legacy blocks through a validated migration before ingestion
When it happens
Trigger: A ProtoTransactionVariant::Mint arrives with input_contract present but output_contract None (or both unset — this error surfaces once input checks pass). Typical with partial encoders or schema-skew between block source and aggregator.
Common situations: Producer node built with a proto version where mint output_contract was optional/newly added; truncated or hand-crafted proto payloads; replaying legacy archived blocks.
Related errors
- Missing input_contract on mint transaction
- Missing purpose on upgrade transaction
- Missing input variant
- Missing utxo_id
- Missing tx_pointer
AI-assisted analysis of FuelLabs/fuel-core@b9d4d170da (2026-08-16).
Data as JSON: /api/errors/fa7b32f4222594f5.
Report an issue: GitHub.