canopy-network/canopy · error · ValueError
Message does not support serialization
Error message
Message does not support serialization
What it means
marshal serializes a protobuf message to bytes via SerializeToString(). If the passed object lacks that method (i.e. it is not a protobuf Message), the function raises ValueError('Message does not support serialization'), which the except block then re-raises wrapped via err_unmarshal. This guards against accidentally passing plain dicts, dataclasses, or None where a protobuf message is required.
Source
Thrown at plugin/python/contract/contract.py:127
def key_for_fee_params() -> bytes:
"""Generate state database key for fee parameters."""
return join_len_prefix(PARAMS_PREFIX, b"/f/")
def key_for_fee_pool(chain_id: int) -> bytes:
"""Generate state database key for fee pool."""
return join_len_prefix(POOL_PREFIX, format_uint64(chain_id))
# Proto marshal/unmarshal utilities
def marshal(message: Any) -> bytes:
"""Marshal object to protobuf bytes."""
try:
if hasattr(message, 'SerializeToString'):
return message.SerializeToString()
raise ValueError("Message does not support serialization")
except Exception as err:
raise err_unmarshal(err)
def unmarshal(message_type: Any, data: Optional[bytes]) -> Optional[Any]:
"""Unmarshal bytes to protobuf message."""
if not data:
return None
try:
if hasattr(message_type, 'FromString'):
return message_type.FromString(data)
raise ValueError("Message type does not support deserialization")
except Exception as err:
raise err_unmarshal(err)
class Contract:
"""View on GitHub (pinned to ee8197d91d)
Solutions
- Ensure you pass an instance of a generated protobuf message (e.g. Account(...) from the generated module), not a dict or the class itself.
- Convert plain data first with a helper that constructs the protobuf message (Account(address=..., amount=...)) before calling marshal.
- Verify the protobuf code generation ran (npm/py protoc step) so the real message classes with SerializeToString exist.
- Catch the wrapped err_unmarshal error at the deliver_tx boundary and return a PluginError response rather than crashing.
Example fix
// before
resp = await plugin.state_write(self, sets=[{'key': k, 'value': marshal({'address': addr, 'amount': amt})}])
// after
msg = Account(address=addr, amount=amt)
resp = await plugin.state_write(self, sets=[{'key': k, 'value': marshal(msg)}]) Defensive patterns
Strategy: type-guard
Validate before calling
def is_protobuf_message(obj) -> bool:
return hasattr(obj, 'SerializeToString') and callable(obj.SerializeToString) Type guard
from google.protobuf.message import Message
def is_message(obj) -> bool:
return isinstance(obj, Message) Try / catch
try:
value = marshal(msg)
except Exception as err:
return PluginDeliverResponse(error=err_unmarshal(err)) Prevention
- Always construct generated protobuf classes, not dicts, for state values.
- Import message types from the generated module, never stubs.
- Run proto code generation in CI so classes always exist.
- Add isinstance checks at boundaries where messages cross modules.
When it happens
Trigger: Calling marshal() with a plain dict, dataclass, bytes, str, or None instead of a protobuf Message instance — e.g. in _deliver_message_send when building a state-write value from a non-protobuf object.
Common situations: Constructing the payload by hand as a dict after refactoring away from generated protobuf classes; a message type that failed to import/generate so a placeholder object is passed; passing the message class instead of an instance.
Understand the failure class
Background: "JSON serialization failed", "not JSON serializable", "Failed to serialize": why JSON marshaling errors happen and how to fix them — this error's family across 46 libraries.
Related errors
- Message type does not support deserialization
- Could not find types.proto descriptor
- Item too long: {len(item)} bytes (max 255)
- Invalid uint64 value: {value}
- 1
AI-assisted analysis of canopy-network/canopy@ee8197d91d (2026-09-06).
Data as JSON: /api/errors/16c3183f2a547bc8.
Report an issue: GitHub.