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

  1. Ensure you pass an instance of a generated protobuf message (e.g. Account(...) from the generated module), not a dict or the class itself.
  2. Convert plain data first with a helper that constructs the protobuf message (Account(address=..., amount=...)) before calling marshal.
  3. Verify the protobuf code generation ran (npm/py protoc step) so the real message classes with SerializeToString exist.
  4. 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

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


AI-assisted analysis of canopy-network/canopy@ee8197d91d (2026-09-06). Data as JSON: /api/errors/16c3183f2a547bc8. Report an issue: GitHub.