{"record":{"id":"cb0141f154b0d320","repo":"micro/go-micro","slug":"failed-to-unmarshal-v-is-not-type-of-proto-messa","errorCode":null,"errorMessage":"failed to unmarshal: %v is not type of proto.Message","messagePattern":"failed to unmarshal: (.+?) is not type of proto\\.Message","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"client/grpc/codec.go","lineNumber":88,"sourceCode":"\t\treturn proto.Marshal(m)\n\tcase protoiface.MessageV1:\n\t\t// #2333 compatible with etcd legacy proto.Message\n\t\tm2 := protoimpl.X.ProtoMessageV2Of(m)\n\t\treturn proto.Marshal(m2)\n\t}\n\treturn nil, fmt.Errorf(\"failed to marshal: %v is not type of *bytes.Frame or proto.Message\", v)\n}\n\nfunc (protoCodec) Unmarshal(data []byte, v interface{}) error {\n\tswitch m := v.(type) {\n\tcase proto.Message:\n\t\treturn proto.Unmarshal(data, m)\n\tcase protoiface.MessageV1:\n\t\t// #2333 compatible with etcd legacy proto.Message\n\t\tm2 := protoimpl.X.ProtoMessageV2Of(m)\n\t\treturn proto.Unmarshal(data, m2)\n\t}\n\treturn fmt.Errorf(\"failed to unmarshal: %v is not type of proto.Message\", v)\n}\n\nfunc (protoCodec) Name() string {\n\treturn \"proto\"\n}\n\nfunc (bytesCodec) Marshal(v interface{}) ([]byte, error) {\n\tb, ok := v.(*[]byte)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"failed to marshal: %v is not type of *[]byte\", v)\n\t}\n\treturn *b, nil\n}\n\nfunc (bytesCodec) Unmarshal(data []byte, v interface{}) error {\n\tb, ok := v.(*[]byte)\n\tif !ok {\n\t\treturn fmt.Errorf(\"failed to unmarshal: %v is not type of *[]byte\", v)","sourceCodeStart":70,"sourceCodeEnd":106,"githubUrl":"https://github.com/micro/go-micro/blob/24529f140421a11a33b6999ab7944f2021cfd69c/client/grpc/codec.go#L70-L106","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a pointer to a generated protobuf message as the response (e.g. &pb.GetResponse{})","Switch the client/service Content-Type to a non-proto codec if payloads are not protobuf","Regenerate protobuf bindings with protoc if handlers use outdated types","Check that both client and server agree on application/grpc+proto"],"exampleFix":"// before\nvar resp map[string]interface{}\nclient.Call(ctx, req, &resp)\n// after\nresp := &mypb.GetResponse{}\nclient.Call(ctx, req, resp)","handlingStrategy":"type-guard","validationCode":"var resp pb.GetResponse\nif _, ok := interface{}(&resp).(proto.Message); !ok {\n    return fmt.Errorf(\"response target must be proto.Message\")\n}","typeGuard":"func isProtoMessagePtr(v interface{}) bool {\n    m, ok := v.(proto.Message)\n    return ok && m != nil\n}","tryCatchPattern":"err := client.Call(ctx, req, resp)\nif err != nil && strings.Contains(err.Error(), \"failed to unmarshal\") {\n    // resp is not proto.Message: fix the response type or codec\n}","preventionTips":["Always pass &pb.Response{} pointers as call targets","Regenerate pb bindings after .proto changes","Keep server/client Content-Types identical","Type-check payload targets in wrapper helpers"],"tags":["grpc","protobuf","codec","serialization"],"backgroundTag":"grpc-codec-type-mismatch","analyzedSha":"24529f140421a11a33b6999ab7944f2021cfd69c","analyzedAt":"2026-09-01T02:52:24.923Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}