{"record":{"id":"ee8563eec8efc365","repo":"canopy-network/canopy","slug":"message-type-does-not-support-deserialization","errorCode":null,"errorMessage":"Message type does not support deserialization","messagePattern":"Message type does not support deserialization","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"plugin/python/contract/contract.py","lineNumber":139,"sourceCode":"\ndef marshal(message: Any) -> bytes:\n    \"\"\"Marshal object to protobuf bytes.\"\"\"\n    try:\n        if hasattr(message, 'SerializeToString'):\n            return message.SerializeToString()\n        raise ValueError(\"Message does not support serialization\")\n    except Exception as err:\n        raise err_unmarshal(err)\n\n\ndef unmarshal(message_type: Any, data: Optional[bytes]) -> Optional[Any]:\n    \"\"\"Unmarshal bytes to protobuf message.\"\"\"\n    if not data:\n        return None\n    try:\n        if hasattr(message_type, 'FromString'):\n            return message_type.FromString(data)\n        raise ValueError(\"Message type does not support deserialization\")\n    except Exception as err:\n        raise err_unmarshal(err)\n\n\nclass Contract:\n    \"\"\"\n    Contract defines the smart contract that implements the extended logic of the nested chain.\n    Matches Go's Contract struct.\n    \"\"\"\n\n    def __init__(\n        self,\n        config: Optional[\"Config\"] = None,\n        fsm_config: Optional[PluginFSMConfig] = None,\n        plugin: Optional[\"Plugin\"] = None,\n        fsm_id: Optional[int] = None,\n    ):\n        self.config = config","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/canopy-network/canopy/blob/ee8197d91dd410f6592cb650a94c925ee6dc8bad/plugin/python/contract/contract.py#L121-L157","documentation":"unmarshal decodes raw bytes into a protobuf message using message_type.FromString(data). If the given message_type lacks FromString (i.e. it is not a generated protobuf message class, or an instance was passed instead of the class), it raises ValueError, re-raised as an err_unmarshal-wrapped error. Callers include check_tx (decoding FeeParams) and _deliver_message_send.","triggerScenarios":"Calling unmarshal(FeeParams_instance, data) with an instance instead of the class; passing a non-protobuf type (dict, type alias, None class) as message_type; the generated module not imported so a stub is used.","commonSituations":"Refactoring renamed the generated message class so the old name resolves to something else; passing `FeeParams()` (instance) rather than `FeeParams` (class); forgetting to regenerate protobuf code so FromString is missing on a hand-written stub.","solutions":["Pass the generated message CLASS (FeeParams), not an instance: min_fees = unmarshal(FeeParams, fee_params_bytes).","Confirm the import points at the generated protobuf module that defines FromString (regenerate code if needed).","Validate message_type has FromString before the call: if not hasattr(T, 'FromString'): raise TypeError(...).","Catch the err_unmarshal-wrapped error in check_tx/deliver_tx and return a PluginError response to the FSM."],"exampleFix":"// before\nmin_fees = unmarshal(FeeParams(), fee_params_bytes)  # instance, has no FromString\n// after\nmin_fees = unmarshal(FeeParams, fee_params_bytes)  # class","handlingStrategy":"type-guard","validationCode":"def is_message_class(t) -> bool:\n    return isinstance(t, type) and hasattr(t, 'FromString')","typeGuard":"from google.protobuf.message import Message\n\ndef is_deserializable(t) -> bool:\n    return isinstance(t, type) and issubclass(t, Message)","tryCatchPattern":"try:\n    min_fees = unmarshal(FeeParams, fee_params_bytes)\nexcept Exception as err:\n    return PluginCheckResponse(error=err_unmarshal(err))","preventionTips":["Pass the message class (FeeParams), never an instance, to unmarshal.","Verify generated protobuf code is up to date after schema edits.","Use issubclass(t, Message) assertions in tests for every decoded type.","Catch wrapped err_unmarshal errors at check_tx/deliver_tx boundaries."],"tags":["python","protobuf","deserialization","type-mismatch"],"backgroundTag":"protobuf-unmarshal-failed","analyzedSha":"ee8197d91dd410f6592cb650a94c925ee6dc8bad","analyzedAt":"2026-09-06T09:30:15.973Z","contentChangedAt":"2026-09-06T09:30:15.973Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}