{"record":{"id":"23a5f4909a2ddcbf","repo":"FuelLabs/fuel-core","slug":"missing-panic-reason","errorCode":null,"errorMessage":"Missing panic reason","messagePattern":"Missing panic reason","errorType":"exception","errorClass":"Error::Serialization","httpStatus":500,"severity":"error","filePath":"crates/services/block_aggregator_api/src/blocks/old_block_source/convertor_adapter/proto_to_fuel_conversions.rs","lineNumber":357,"sourceCode":"                ))\n            })?;\n            Ok(FuelReceipt::return_data_with_len(\n                id,\n                rd.ptr,\n                rd.len,\n                digest,\n                rd.pc,\n                rd.is,\n                rd.data.clone(),\n            ))\n        }\n        ProtoReceiptVariant::Panic(panic_receipt) => {\n            let id = ContractId::try_from(panic_receipt.id.as_slice())\n                .map_err(|e| Error::Serialization(anyhow!(e)))?;\n            let reason_proto = panic_receipt\n                .reason\n                .as_ref()\n                .ok_or_else(|| Error::Serialization(anyhow!(\"Missing panic reason\")))?;\n            let reason = panic_instruction_from_proto(reason_proto);\n            let contract_id = panic_receipt\n                .contract_id\n                .as_ref()\n                .map(|cid| {\n                    ContractId::try_from(cid.as_slice())\n                        .map_err(|e| Error::Serialization(anyhow!(e)))\n                })\n                .transpose()?;\n            Ok(\n                FuelReceipt::panic(id, reason, panic_receipt.pc, panic_receipt.is)\n                    .with_panic_contract_id(contract_id),\n            )\n        }\n        ProtoReceiptVariant::Revert(revert) => {\n            let id = ContractId::try_from(revert.id.as_slice())\n                .map_err(|e| Error::Serialization(anyhow!(e)))?;\n            Ok(FuelReceipt::revert(id, revert.ra, revert.pc, revert.is))","sourceCodeStart":339,"sourceCodeEnd":375,"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#L339-L375","documentation":"receipt_from_proto requires the Panic receipt's reason sub-message; prost yields None when the field was never set, and this code maps it to Error::Serialization. The reason is then fed to panic_instruction_from_proto, which itself tolerates unknown enum values (defaults to Unknown) - but the sub-message itself must exist.","triggerScenarios":"Decoding a proto block where a Panic receipt was encoded without its reason sub-message - an encoder that skips the field, a truncated message, or a fixture built with Default::default().","commonSituations":"Version skew between fuel-core versions that added or renamed panic-receipt fields; third-party producers; fuzzed input where nested sub-messages are dropped.","solutions":["Debug-print the failing Panic receipt to confirm reason is None.","Fix the encoder to always attach a PanicReason (even Unknown) when writing panic receipts.","Pre-validate presence of reason on Panic receipts and reject with position context.","Re-fetch the block from a compatible peer."],"exampleFix":"// before\nlet reason_proto = panic_receipt.reason.as_ref().ok_or_else(|| {\n    Error::Serialization(anyhow!(\"Missing panic reason\"))\n})?;\n\n// after: validate before decoding, keep position context\nif let ProtoReceiptVariant::Panic(p) = variant {\n    if p.reason.is_none() {\n        return Err(Error::Serialization(anyhow::anyhow!(\n            \"receipt[{i}] Panic receipt missing reason sub-message\"\n        )));\n    }\n}","handlingStrategy":"type-guard","validationCode":"fn panic_receipt_has_reason(p: &ProtoPanic) -> bool {\n    p.reason.is_some()\n}","typeGuard":"fn panic_receipt_complete(p: &ProtoPanic) -> bool {\n    p.reason.is_some()\n}","tryCatchPattern":"match receipt_from_proto(r) {\n    Ok(receipt) => out.push(receipt),\n    Err(Error::Serialization(ctx)) if ctx.to_string().contains(\"panic reason\") => {\n        tracing::warn!(?r, \"rejecting Panic receipt without reason\");\n        return Err(Error::Serialization(ctx));\n    }\n    Err(e) => return Err(e),\n}","preventionTips":["Encoders must attach a reason sub-message to every panic receipt (even ProtoPanicReason::Unknown).","Pre-validate Panic receipts after decode and before conversion.","Watch version upgrades that add receipt sub-fields (e.g. panic contract_id) for presence regressions."],"tags":["rust","fuel-core","protobuf","deserialization","receipts","missing-field"],"backgroundTag":null,"analyzedSha":"b9d4d170da3a31c9ace5f963d633b326348e0d42","analyzedAt":"2026-08-16T08:56:42.692Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}