go-kratos/kratos · warning
unsupported message type: %q
Error message
unsupported message type: %q
What it means
Form ENcoder error (EncodeField -> encodeMessage) mirroring the decoder limit: when encoding a proto message to form/query output, a message-typed field whose full name is not one of the supported well-known types (Timestamp, Duration, BytesValue, the scalar wrappers, FieldMask) hits the default and fails. Custom message fields and unsupported WKTs (Any, Struct, Value, Empty) cannot be encoded into the flat string form.
Source
Thrown at encoding/form/proto_encode.go:168
return marshalDuration(value.Message())
case bytesMessageFullname:
return marshalBytes(value.Message())
case "google.protobuf.DoubleValue", "google.protobuf.FloatValue", "google.protobuf.Int64Value", "google.protobuf.Int32Value",
"google.protobuf.UInt64Value", "google.protobuf.UInt32Value", "google.protobuf.BoolValue", "google.protobuf.StringValue":
fd := msgDescriptor.Fields()
v := value.Message().Get(fd.ByName("value"))
return fmt.Sprint(v.Interface()), nil
case fieldMaskFullName:
m, ok := value.Message().Interface().(*fieldmaskpb.FieldMask)
if !ok || m == nil {
return "", nil
}
for i, v := range m.Paths {
m.Paths[i] = jsonCamelCase(v)
}
return strings.Join(m.Paths, ","), nil
default:
return "", fmt.Errorf("unsupported message type: %q", string(msgDescriptor.FullName()))
}
}
// EncodeFieldMask return field mask name=paths
func EncodeFieldMask(m protoreflect.Message) (query string) {
m.Range(func(fd protoreflect.FieldDescriptor, v protoreflect.Value) bool {
if fd.Kind() == protoreflect.MessageKind {
if msg := fd.Message(); msg.FullName() == fieldMaskFullName {
value, err := encodeMessage(msg, v)
if err != nil {
return false
}
if fd.HasJSONName() {
query = fd.JSONName() + "=" + value
} else {
query = fd.TextName() + "=" + value
}
return falseView on GitHub (pinned to 668db92c2c)
Solutions
- Keep form-encoded messages limited to scalar fields and supported WKTs (Timestamp, Duration, wrappers, FieldMask, BytesValue)
- Flatten structured data into prefixed scalar fields for query-param APIs, or move the data to a JSON body via POST
- For JSON-shaped values use google.protobuf.Value/Struct on the wire - but note the ENcoder does not support them either; encode the JSON string yourself into a string field
- Write a custom encoder case (or pre-encode the submessage to JSON into a string field) if the field must stay a message
Example fix
// before: message with custom message field
// message Req { Address address = 1; }
// form.EncodeField -> unsupported message type: "pkg.Address"
// after: flatten for form encoding
// message Req { string address_city = 1; string address_country = 2; } Defensive patterns
Strategy: validation
Validate before calling
// Before encoding to form, assert no unsupported message fields exist
func encodableToForm(msg protoreflect.Message) error {
var err error
msg.Descriptor().Fields().Range(func(fd protoreflect.FieldDescriptor) bool {
if fd.Kind() == protoreflect.MessageKind && !isSupportedFormMessage(fd.Message().FullName()) {
err = fmt.Errorf("field %q of type %s cannot be form-encoded", fd.Name(), fd.Message().FullName())
return false
}
return true
})
return err
} Type guard
func isSupportedFormMessage(fullName protoreflect.FullName) bool {
switch fullName {
case "google.protobuf.Timestamp", "google.protobuf.Duration",
"google.protobuf.BytesValue", "google.protobuf.FieldMask",
"google.protobuf.DoubleValue", "google.protobuf.FloatValue",
"google.protobuf.Int64Value", "google.protobuf.Int32Value",
"google.protobuf.UInt64Value", "google.protobuf.UInt32Value",
"google.protobuf.BoolValue", "google.protobuf.StringValue":
return true
}
return false
} Try / catch
if q, err := form.EncodeQuery(msg); err != nil {
if strings.Contains(err.Error(), "unsupported message type") {
// fall back to JSON body for this call
req.SetBodyProtoJSON(msg)
return nil
}
return err
} Prevention
- Generate request builders from proto and encode only form-safe fields into queries
- Keep structured payloads in bodies (JSON), scalars in query strings
- Add a lint rule that GET-endpoint protos contain no custom message fields
- Unit-test form encoding of every request message type in the client SDK
When it happens
Trigger: Calling form encoding (form.EncodeField / building query strings from a proto message, e.g. generating request URLs or form bodies from messages) where a field's type is a user-defined message like `Address address = 1` or `google.protobuf.Any data = 2`. EncodeField dispatches MessageKind to encodeMessage, whose switch has no case for that full name.
Common situations: Client-side request builders encoding proto request messages into query strings; Kratos HTTP clients generating query params for GET endpoints whose proto grew structured fields; test helpers serializing full messages into forms; mirroring a JSON API into GET params after adding nested types.
Related errors
- unsupported message type: %q
- invalid path: %q is not a message
- field already set for oneof %q
- too many values for field %q: %s
- parsing field %q: %w
AI-assisted analysis of go-kratos/kratos@668db92c2c (2026-08-16).
Data as JSON: /api/errors/49091bd838284d3a.
Report an issue: GitHub.