0
votes

I'm trying to send a proto3 struct to a gRPC server with Go. The struct has an oneof type, which I seem to populate fine. When sending the message to my gRPC client, I get a panic about invalid memory address or nil pointer reference.

I have the proto definition (full file is at https://github.com/MovingGauteng/geofancy-rs/blob/master/proto/geofancy.proto:

# proto3

message Document {
  string collection = 1;
  string id = 2;
  oneof geo {
    Point point = 4;
    LineString line = 5;
    Bounds bounds = 6;
    string geojson = 7;
  }
}

message Point { Coordinate coord = 1; }

message Coordinate {
  double lat = 1;
  double lng = 2;
}

The Go code is:

// create a coordinate
coord := geofancy.Coordinate{
  Lat: 10.2,
  Lng: -15.3,
}
// add coordinate to a point
point := geofancy.Point{
  Coord: &coord,
}

// these statements print valid data
log.Printf("Coord: %v", coord)
log.Printf("Point: %v", point)

// create document, add point to the oneof field
document := geofancy.Document{
  Collection:   "some-collection",
  Id:           "some-id",
  Geo:          &point,
}

// this prints the document correctly
log.Printf("Document: %v", document)

// connect to client
var opts []grpc.DialOption
opts = append(opts, grpc.WithInsecure())
var err error
conn, err := grpc.Dial("my-server:port", opts...)
if err != nil {
    log.Fatalf("fail to dial: %v", err)
}
defer conn.Close()
client := geofancy.NewGeofancyServiceClient(conn)

ctx, cancel := context.WithTimeout(context.Background(), 5*time.Second)
defer cancel()

// this panics each time
res, err := client.SetDocument(ctx, &document)

The stack trace looks like:

panic: runtime error: invalid memory address or nil pointer dereference
[signal 0xc0000005 code=0x0 addr=0x10 pc=0x54d731]

goroutine 1 [running]:
my-project/vendor/github.com/golang/protobuf/proto.makeOneOfMarshaler.func1(0xc000068ce0, 0x0, 0x0)
        D:/projects/golang/src/my-project/vendor/github.com/golang/protobuf/proto/table_marshal.go:2373 +0x131
my-project/vendor/github.com/golang/protobuf/proto.(*marshalInfo).size(0xc0003021c0, 0xc000068cc0, 0xf9ef40)
        D:/projects/golang/src/my-project/vendor/github.com/golang/protobuf/proto/table_marshal.go:183 +0xa8
my-project/vendor/github.com/golang/protobuf/proto.(*InternalMessageInfo).Size(0xf9ef40, 0xae7fa0, 0xc000068cc0, 0x30448c0)
        D:/projects/golang/src/my-project/vendor/github.com/golang/protobuf/proto/table_marshal.go:125 +0x6e
my-project/vendor/my-protos.(*Document).XXX_Size(0xc000068cc0, 0xae7fa0)
        D:/projects/golang/src/my-project/vendor/my-protos/geofancy.pb.go:197 +0x4a
my-project/vendor/github.com/golang/protobuf/proto.(*Buffer).Marshal(0xc0002de698, 0xae7fa0, 0xc000068cc0, 0xc0000363f0, 0xc000068cc0)
        D:/projects/golang/src/my-project/vendor/github.com/golang/protobuf/proto/table_marshal.go:2740 +0x8c
my-project/vendor/google.golang.org/grpc/encoding/proto.marshal(0xa0c520, 0xc000068cc0, 0xc0002de690, 0x0, 0x0, 0xc000006000, 0x99a900, 0x1)        
        D:/projects/golang/src/my-project/vendor/google.golang.org/grpc/encoding/proto/proto.go:59 +0xe9
my-project/vendor/google.golang.org/grpc/encoding/proto.codec.Marshal(0xa0c520, 0xc000068cc0, 0x0, 0x2030000, 0x2030000, 0x30000, 0x20)
        D:/projects/golang/src/my-project/vendor/google.golang.org/grpc/encoding/proto/proto.go:74 +0xb4
my-project/vendor/google.golang.org/grpc.encode(0x3080168, 0xfbc400, 0xa0c520, 0xc000068cc0, 0xc000310020, 0xc000362000, 0x11d0008, 0x0, 0x0)       
        D:/projects/golang/src/my-project/vendor/google.golang.org/grpc/rpc_util.go:543 +0x59
my-project/vendor/google.golang.org/grpc.prepareMsg(0xa0c520, 0xc000068cc0, 0x3080168, 0xfbc400, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, ...)
        D:/projects/golang/src/my-project/vendor/google.golang.org/grpc/stream.go:1501 +0x8c
my-project/vendor/google.golang.org/grpc.(*clientStream).SendMsg(0xc00032e000, 0xa0c520, 0xc000068cc0, 0x0, 0x0)
        D:/projects/golang/src/my-project/vendor/google.golang.org/grpc/stream.go:681 +0x157
my-project/vendor/google.golang.org/grpc.invoke(0xaed320, 0xc000068f60, 0xa42207, 0x25, 0xa0c520, 0xc000068cc0, 0x9e3000, 0xc00003c840, 0xc000065180, 0x0, ...)
        D:/projects/golang/src/my-project/vendor/google.golang.org/grpc/call.go:70 +0xee
my-project/vendor/google.golang.org/grpc.(*ClientConn).Invoke(0xc000065180, 0xaed320, 0xc000068f60, 0xa42207, 0x25, 0xa0c520, 0xc000068cc0, 0x9e3000, 0xc00003c840, 0x0, ...)
        D:/projects/golang/src/my-project/vendor/google.golang.org/grpc/call.go:37 +0x1bb
my-project/vendor/my-protos.(*geofancyServiceClient).SetDocument(0xc000006030, 0xaed320, 0xc000068f60, 0xc000068cc0, 0x0, 0x0, 0x0, 0xc000065180, 0x0, 0x0)
        D:/projects/golang/src/my-project/vendor/my-protos/geofancy.pb.go:925 +0xd9
main.sendMyMessage(0xc0002e4000, 0x77359400)
        D:/projects/golang/src/my-project/main.go:89 +0x685
main.main()
        D:/projects/golang/src/my-project/main.go:117 +0xf4
1
Can you share the complete stacktrace?Mithun Arunan
I've added it @MithunArunannevi_me
I wonder if it has something to do with your vendoring. Try removing the vendor directory and use everything vanilla and see what happens. E.g. maybe you have an incompatible combination of generated code and the proto library?Doug Fawley

1 Answers

1
votes

I am surprised your code is compiling. I was so intrigued that I generated the Go code in my environment to understand it.

The "geofancy.Document" struct cannot receive a Point as a value for the attribute Geo because it expects a generic "Document Geo" (that could be a Document_Point, a Document_LineString, etc...)

The auto generated code has an interface that you can use in this case.

    // add coordinate to a point
    point := geofancy.Document_Point{
        Point: &geofancy.Point{
            Coord: &coord,
        },
    }

Now your variable point is an struct that implements an interface acceptable by Geo.

document := geofancy.Document{
  Collection:   "some-collection",
  Id:           "some-id",
  Geo:          &point,
}

I was not able to compile using your example because the was getting the error:

 cannot use &point (type **geofancy.Point) as type geofancy.isDocument_Geo in field value:
    **geofancy.Point does not implement geofancy.isDocument_Geo (missing geofancy.isDocument_Geo method)```