{"record":{"id":"a6526bdf6f6af260","repo":"grpc/grpc-go","slug":"proto-failed-to-marshal-message-is-t-want-prot","errorCode":null,"errorMessage":"proto: failed to marshal, message is %T, want proto.Message","messagePattern":"proto: failed to marshal, message is %T, want proto\\.Message","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"encoding/proto/proto.go","lineNumber":46,"sourceCode":"\t\"google.golang.org/protobuf/proto\"\n\t\"google.golang.org/protobuf/protoadapt\"\n)\n\n// Name is the name registered for the proto compressor.\nconst Name = \"proto\"\n\nfunc init() {\n\tencoding.RegisterCodecV2(&codecV2{})\n}\n\n// codec is a CodecV2 implementation with protobuf. It is the default codec for\n// gRPC.\ntype codecV2 struct{}\n\nfunc (c *codecV2) Marshal(v any) (data mem.BufferSlice, err error) {\n\tvv := messageV2Of(v)\n\tif vv == nil {\n\t\treturn nil, fmt.Errorf(\"proto: failed to marshal, message is %T, want proto.Message\", v)\n\t}\n\n\t// Important: if we remove this Size call then we cannot use\n\t// UseCachedSize in MarshalOptions below.\n\tsize := proto.Size(vv)\n\n\t// MarshalOptions with UseCachedSize allows reusing the result from the\n\t// previous Size call. This is safe here because:\n\t//\n\t// 1. We just computed the size.\n\t// 2. We assume the message is not being mutated concurrently.\n\t//\n\t// Important: If the proto.Size call above is removed, using UseCachedSize\n\t// becomes unsafe and may lead to incorrect marshaling.\n\t//\n\t// For more details, see the doc of UseCachedSize:\n\t// https://pkg.go.dev/google.golang.org/protobuf/proto#MarshalOptions\n\tmarshalOptions := proto.MarshalOptions{UseCachedSize: true}","sourceCodeStart":28,"sourceCodeEnd":64,"githubUrl":"https://github.com/grpc/grpc-go/blob/03255a9237b6eb32710f6bc4f2de9a675b99fe36/encoding/proto/proto.go#L28-L64","documentation":"The default proto codec's Marshal received a value that is neither a proto.MessageV1 (legacy github.com/golang/protobuf) nor a proto.MessageV2 (google.golang.org/protobuf), as determined by messageV2Of returning nil. gRPC's default wire format is protobuf; sending a non-proto Go value (a plain struct, map, string, etc.) triggers this on the sending side.","triggerScenarios":"Using grpc.UnaryClientInterceptor or a generic client to call client.Invoke with a non-proto request struct; passing a *struct{} or a custom message type that does not embed proto message methods; mixing grpc-go with a hand-rolled codec but forgetting to register it with encoding.RegisterCodec.","commonSituations":"Beginner mistake of trying to send a Go map or plain struct over gRPC; generating stubs with an outdated protoc plugin that produced V1 messages but the binary also lacks the V1->V2 adapter; sending the empty interface value nil.","solutions":["Make sure every RPC request/response is a generated protobuf message type (import the generated *.pb.go and use its structs).","If you intentionally want non-proto payloads, register a custom encoding.Codec (e.g. a JSON codec named \"json\") and set the :content-type metadata / WithDefaultCallOptions(ForceCodec(...)).","If you must use legacy github.com/golang/protobuf messages, ensure that module is imported so protoadapt.MessageV1Of can adapt them."],"exampleFix":"// before\ntype Ping struct{ Msg string } // not a proto message\nclient.Invoke(ctx, \"/svc/Do\", &Ping{Msg:\"hi\"}, &Pong{})\n\n// after\n// after generating pb.go: client.Invoke(ctx, \"/svc/Do\", &pb.Ping{Msg:\"hi\"}, &pb.Pong{})","handlingStrategy":"type-guard","validationCode":"import (\n    \"google.golang.org/protobuf/proto\"\n    \"google.golang.org/protobuf/protoadapt\"\n)\n\nfunc isProtoMessage(v any) bool {\n    switch v.(type) {\n    case protoadapt.MessageV1, protoadapt.MessageV2, proto.Message:\n        return true\n    }\n    return false\n}","typeGuard":"func isMarshalableProto(v any) bool {\n    switch v.(type) {\n    case protoadapt.MessageV1, protoadapt.MessageV2:\n        return true\n    }\n    return false\n}","tryCatchPattern":"data, err := proto.Marshal(m) // or via codec\nif err != nil { return fmt.Errorf(\"marshal: %w\", err) }","preventionTips":["Always use generated *pb.Message types for RPC payloads.","Register a custom codec (e.g. json) if you must send non-proto payloads and force it via ForceCodec.","Keep github.com/golang/protobuf imported if you interop with V1 messages."],"tags":["grpc","proto","codec","marshal","encoding","serialization"],"analyzedSha":"03255a9237b6eb32710f6bc4f2de9a675b99fe36","analyzedAt":"2026-08-07T00:29:34.215Z","schemaVersion":2},"datasetVersion":"2026-08-07T03:17:09.362Z"}