{"record":{"id":"84bf757c7bd54782","repo":"FuelLabs/fuel-core","slug":"missing-input-contract-on-mint-transaction","errorCode":null,"errorMessage":"Missing input_contract on mint transaction","messagePattern":"Missing input_contract on mint transaction","errorType":"exception","errorClass":"Error::Serialization","httpStatus":null,"severity":"error","filePath":"crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs","lineNumber":677,"sourceCode":"                bytecode_witness_index,\n                policies,\n                salt,\n                storage_slots,\n                inputs,\n                outputs,\n                witnesses,\n            );\n\n            Ok(FuelTransaction::Create(create_tx))\n        }\n        ProtoTransactionVariant::Mint(proto_mint) => {\n            let tx_pointer_proto = proto_mint.tx_pointer.as_ref().ok_or_else(|| {\n                Error::Serialization(anyhow!(\"Missing tx_pointer on mint transaction\"))\n            })?;\n            let tx_pointer = tx_pointer_from_proto(tx_pointer_proto)?;\n            let input_contract_proto =\n                proto_mint.input_contract.as_ref().ok_or_else(|| {\n                    Error::Serialization(anyhow!(\n                        \"Missing input_contract on mint transaction\"\n                    ))\n                })?;\n            let input_contract = contract_input_from_proto(input_contract_proto)?;\n            let output_contract_proto =\n                proto_mint.output_contract.as_ref().ok_or_else(|| {\n                    Error::Serialization(anyhow!(\n                        \"Missing output_contract on mint transaction\"\n                    ))\n                })?;\n            let output_contract = contract_output_from_proto(output_contract_proto)?;\n            let mint_asset_id = fuel_core_types::fuel_types::AssetId::try_from(\n                proto_mint.mint_asset_id.as_slice(),\n            )\n            .map_err(|e| Error::Serialization(anyhow!(e)))?;\n\n            let mint_tx = FuelTransaction::mint(\n                tx_pointer,","sourceCodeStart":659,"sourceCodeEnd":695,"githubUrl":"https://github.com/FuelLabs/fuel-core/blob/b9d4d170da3a31c9ace5f963d633b326348e0d42/crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs#L659-L695","documentation":"Thrown while converting a protobuf-encoded Mint transaction into the fuel-core Mint type. The Mint variant requires a contract input; the proto message arrived with the optional input_contract field unset (None). The convertor refuses to guess and fails with Error::Serialization, aborting the whole transaction (and typically the block) conversion.","triggerScenarios":"Decoding a ProtoTransactionVariant::Mint message whose input_contract oneof/optional field was never set by the producer. Happens when the peer or stored data was serialized by an older/different proto schema that omitted mint contract fields, or when a hand-built/test proto message skipped it.","commonSituations":"Version skew between the node that produced the proto block data and this aggregator's proto definitions; importing archived blocks encoded before Mint gained input_contract; test fixtures built from partial protos.","solutions":["Regenerate or replay the mint transaction with input_contract populated (producer side) and resend the block","Check proto/schema version alignment between the block source and this crate (protobuf_types.rs) and upgrade the mismatched side","If old data must be read, migrate/re-encode it with a tool that fills a default contract input before conversion","Add a pre-conversion validation pass that rejects/skips mint transactions missing input_contract so one bad tx does not abort the block"],"exampleFix":"// before (producer, Rust prost build)\nlet mint = ProtoMint { tx_pointer: Some(tp), mint_amount: 1000, ..Default::default() }; // input_contract: None\n\n// after\nlet mint = ProtoMint {\n    tx_pointer: Some(tp),\n    input_contract: Some(proto_contract_input),\n    output_contract: Some(proto_contract_output),\n    mint_amount: 1000,\n    ..Default::default()\n};","handlingStrategy":"validation","validationCode":"fn mint_tx_is_complete(proto_tx: &ProtoTransaction) -> bool {\n    matches!(proto_tx.variant.as_deref(), Some(ProtoTransactionVariant::Mint(m))\n        if m.input_contract.is_some())\n}","typeGuard":"fn has_input_contract(m: &ProtoMint) -> bool { m.input_contract.is_some() }","tryCatchPattern":"match convert_transaction(&proto_tx) {\n    Ok(tx) => use_tx(tx),\n    Err(e) if e.to_string().contains(\"Missing input_contract on mint\") => {\n        quarantine_block(block_id, e); // do not abort the whole ingest\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Pin the same proto schema version on producer and aggregator","Round-trip test (encode then decode) every transaction variant in CI","Never default-construct ProtoMint without its contract fields"],"tags":["rust","fuel","protobuf","serialization","mint-transaction","conversion"],"backgroundTag":null,"analyzedSha":"b9d4d170da3a31c9ace5f963d633b326348e0d42","analyzedAt":"2026-08-16T08:56:42.692Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}