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 false

View on GitHub (pinned to 668db92c2c)

Solutions

  1. Keep form-encoded messages limited to scalar fields and supported WKTs (Timestamp, Duration, wrappers, FieldMask, BytesValue)
  2. Flatten structured data into prefixed scalar fields for query-param APIs, or move the data to a JSON body via POST
  3. 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
  4. 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

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


AI-assisted analysis of go-kratos/kratos@668db92c2c (2026-08-16). Data as JSON: /api/errors/49091bd838284d3a. Report an issue: GitHub.