micro/go-micro · error

failed to unmarshal: %v is not type of proto.Message

Error message

failed to unmarshal: %v is not type of proto.Message

What it means

The proto codec Unmarshal received bytes and a destination value that is not a proto.Message (v2) or MessageV1 (legacy). gRPC responses must decode into protobuf message pointers; anything else cannot be filled by proto.Unmarshal.

Source

Thrown at client/grpc/codec.go:88

		return proto.Marshal(m)
	case protoiface.MessageV1:
		// #2333 compatible with etcd legacy proto.Message
		m2 := protoimpl.X.ProtoMessageV2Of(m)
		return proto.Marshal(m2)
	}
	return nil, fmt.Errorf("failed to marshal: %v is not type of *bytes.Frame or proto.Message", v)
}

func (protoCodec) Unmarshal(data []byte, v interface{}) error {
	switch m := v.(type) {
	case proto.Message:
		return proto.Unmarshal(data, m)
	case protoiface.MessageV1:
		// #2333 compatible with etcd legacy proto.Message
		m2 := protoimpl.X.ProtoMessageV2Of(m)
		return proto.Unmarshal(data, m2)
	}
	return fmt.Errorf("failed to unmarshal: %v is not type of proto.Message", v)
}

func (protoCodec) Name() string {
	return "proto"
}

func (bytesCodec) Marshal(v interface{}) ([]byte, error) {
	b, ok := v.(*[]byte)
	if !ok {
		return nil, fmt.Errorf("failed to marshal: %v is not type of *[]byte", v)
	}
	return *b, nil
}

func (bytesCodec) Unmarshal(data []byte, v interface{}) error {
	b, ok := v.(*[]byte)
	if !ok {
		return fmt.Errorf("failed to unmarshal: %v is not type of *[]byte", v)

View on GitHub (pinned to 24529f1404)

Solutions

  1. Pass a pointer to a generated protobuf message as the response (e.g. &pb.GetResponse{})
  2. Switch the client/service Content-Type to a non-proto codec if payloads are not protobuf
  3. Regenerate protobuf bindings with protoc if handlers use outdated types
  4. Check that both client and server agree on application/grpc+proto

Example fix

// before
var resp map[string]interface{}
client.Call(ctx, req, &resp)
// after
resp := &mypb.GetResponse{}
client.Call(ctx, req, resp)
Defensive patterns

Strategy: type-guard

Validate before calling

var resp pb.GetResponse
if _, ok := interface{}(&resp).(proto.Message); !ok {
    return fmt.Errorf("response target must be proto.Message")
}

Type guard

func isProtoMessagePtr(v interface{}) bool {
    m, ok := v.(proto.Message)
    return ok && m != nil
}

Try / catch

err := client.Call(ctx, req, resp)
if err != nil && strings.Contains(err.Error(), "failed to unmarshal") {
    // resp is not proto.Message: fix the response type or codec
}

Prevention

When it happens

Trigger: Making a gRPC call where the response prototype passed to the client is a plain struct, map, or *interface{} instead of a generated protobuf message pointer, while the codec is proto.

Common situations: Using grpc.NewClient but building requests/responses manually with non-pb types; code migrated from a JSON client where resp was a plain struct; forgotten protoc regeneration so the handler uses the wrong type.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/cb0141f154b0d320. Report an issue: GitHub.