canopy-network/canopy · error
unknown parameter type %T
Error message
unknown parameter type %T
What it means
MessageChangeParameter.MarshalJSON serializes the parameter value only when it is a *lib.StringWrapper or *lib.UInt64Wrapper (stored in a protobuf Any). Any other wrapped type hits the default branch and returns 'unknown parameter type %T'.
Source
Thrown at fsm/message_helpers.go:355
return ErrInvalidProposalHash()
}
return nil
}
// MarshalJSON() is the json.Marshaller implementation for MessageChangeParameter
func (x MessageChangeParameter) MarshalJSON() ([]byte, error) {
a, err := lib.FromAny(x.ParameterValue)
if err != nil {
return nil, err
}
var parameterValue any
switch p := a.(type) {
case *lib.StringWrapper:
parameterValue = p.Value
case *lib.UInt64Wrapper:
parameterValue = p.Value
default:
return nil, fmt.Errorf("unknown parameter type %T", p)
}
return json.Marshal(jsonMessageChangeParameter{
ParameterSpace: x.ParameterSpace,
ParameterKey: x.ParameterKey,
ParameterValue: parameterValue,
StartHeight: x.StartHeight,
EndHeight: x.EndHeight,
Signer: x.Signer,
ProposalHash: x.ProposalHash,
})
}
// UnmarshalJSON() is the json.Unmarshaler implementation for MessageChangeParameter
func (x *MessageChangeParameter) UnmarshalJSON(b []byte) (err error) {
var j jsonMessageChangeParameter
if err = json.Unmarshal(b, &j); err != nil {
return
}View on GitHub (pinned to ee8197d91d)
Solutions
- Add a case for the actual wrapper type in the type switch in MarshalJSON (fsm/message_helpers.go:~347)
- Convert the parameter to StringWrapper or UInt64Wrapper before storing/submitting the ChangeParameter message
- Check proto definitions to confirm which wrapper type is used and align client-side serialization
Example fix
// before
case *lib.UInt64Wrapper:
parameterValue = p.Value
default:
return nil, fmt.Errorf("unknown parameter type %T", p)
// after
case *lib.UInt64Wrapper:
parameterValue = p.Value
case *lib.BoolWrapper:
parameterValue = p.Value
default:
return nil, fmt.Errorf("unknown parameter type %T", p) Defensive patterns
Strategy: type-guard
Validate before calling
switch v := param.Value.(type) {
case *lib.StringWrapper, *lib.UInt64Wrapper:
// ok, marshalable
default:
return fmt.Errorf("unsupported param wrapper %T", v)
} Type guard
func marshalableParam(a *anypb.Any) bool {
var s lib.StringWrapper; var u lib.UInt64Wrapper
return a != nil && (a.UnmarshalTo(&s) == nil || a.UnmarshalTo(&u) == nil)
} Try / catch
b, err := json.Marshal(msg)
if err != nil && strings.Contains(err.Error(), "unknown parameter type") {
// reject/normalize the parameter wrapper before retrying
} Prevention
- Only store StringWrapper or UInt64Wrapper as ChangeParameter values
- Add a case to the type switch whenever a new wrapper type is introduced
- Keep client and node versions in sync on parameter wrapper types
When it happens
Trigger: json.Marshal is called on a MessageChangeParameter whose Any payload contains a wrapper type other than StringWrapper/UInt64Wrapper (e.g. BoolWrapper, BytesWrapper, or a custom message).
Common situations: A governance parameter was stored with a new wrapper kind added to the proto but the JSON marshaling switch was not updated, or an older/newer node version produces a wrapper type this code doesn't know.
Understand the failure class
Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.
Related errors
AI-assisted analysis of canopy-network/canopy@ee8197d91d (2026-09-06).
Data as JSON: /api/errors/989c19de16df6304.
Report an issue: GitHub.