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

  1. Add a case for the actual wrapper type in the type switch in MarshalJSON (fsm/message_helpers.go:~347)
  2. Convert the parameter to StringWrapper or UInt64Wrapper before storing/submitting the ChangeParameter message
  3. 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

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.