{"id":"6f2c7dc3c9692c55","repo":"gin-gonic/gin","slug":"obj-is-not-protomessage","errorCode":null,"errorMessage":"obj is not ProtoMessage","messagePattern":"obj is not ProtoMessage","errorType":"validation","errorClass":null,"httpStatus":400,"severity":"error","filePath":"binding/protobuf.go","lineNumber":32,"sourceCode":"\ntype protobufBinding struct{}\n\nfunc (protobufBinding) Name() string {\n\treturn \"protobuf\"\n}\n\nfunc (b protobufBinding) Bind(req *http.Request, obj any) error {\n\tbuf, err := io.ReadAll(req.Body)\n\tif err != nil {\n\t\treturn err\n\t}\n\treturn b.BindBody(buf, obj)\n}\n\nfunc (protobufBinding) BindBody(body []byte, obj any) error {\n\tmsg, ok := obj.(proto.Message)\n\tif !ok {\n\t\treturn errors.New(\"obj is not ProtoMessage\")\n\t}\n\tif err := proto.Unmarshal(body, msg); err != nil {\n\t\treturn err\n\t}\n\t// Here it's same to return validate(obj), but until now we can't add\n\t// `binding:\"\"` to the struct which automatically generate by gen-proto\n\treturn nil\n\t// return validate(obj)\n}\n","sourceCodeStart":14,"sourceCodeEnd":42,"githubUrl":"https://github.com/gin-gonic/gin/blob/34dac209ffb6ef85cc78c5d217bbb7ad001d68fd/binding/protobuf.go#L14-L42","documentation":"Returned by protobufBinding.BindBody (binding/protobuf.go:32) when the destination object does not implement google.golang.org/protobuf/proto.Message. Protobuf unmarshalling requires a concrete generated message so it knows the wire format; a plain struct or *string fails the type assertion.","triggerScenarios":"Calling c.ProtoBuf / binding.Protobuf.Bind with a struct that was not generated by protoc-gen-go (or is a pointer to one); passing a value instead of a pointer to the generated message.","commonSituations":"Using the older github.com/golang/protobuf/proto.Message alias with a new-style generated type; passing a hand-written struct; passing &someStruct{} instead of &pb.SomeMessage{}.","solutions":["Make sure the bind target is a pointer to a protoc-gen-go generated message, e.g. var msg pb.User; c.ShouldBindWith(&msg, binding.Protobuf).","Regenerate the .pb.go files with the current google.golang.org/protobuf toolchain so the type satisfies proto.Message.","Pass a pointer (&msg) — the unexported ProtoReflect method is only on the pointer receiver for most generated types."],"exampleFix":"// before\nvar msg MyPlainStruct\nc.ProtoBuf(http.StatusOK, &msg)\n// after\nvar msg pb.MyMessage\nc.ShouldBindWith(&msg, binding.Protobuf)","handlingStrategy":"type-guard","validationCode":"var msg any = &pb.MyMessage{}\nif _, ok := msg.(proto.Message); !ok {\n    return fmt.Errorf(\"obj %T is not proto.Message\", msg)\n}","typeGuard":"func isProtoMessage(v any) bool {\n    _, ok := v.(proto.Message)\n    return ok\n}","tryCatchPattern":"if err := c.ShouldBindWith(&msg, binding.Protobuf); err != nil {\n    if err.Error() == \"obj is not ProtoMessage\" {\n        // regenerate pb types or pass a pointer to a generated message\n    }\n}","preventionTips":["Always pass a pointer to a protoc-gen-go generated message (&pb.X{}).","Regenerate .pb.go with google.golang.org/protobuf when upgrading the toolchain.","Add a compile-time check: var _ proto.Message = (*pb.MyMessage)(nil)."],"tags":["binding","protobuf","go"],"analyzedSha":"34dac209ffb6ef85cc78c5d217bbb7ad001d68fd","analyzedAt":"2026-08-04T21:26:18.438Z","schemaVersion":2}