{"record":{"id":"a7858d33fb0ecc10","repo":"go-kratos/kratos","slug":"failed-to-marshal-message-is-t-want-proto-messa","errorCode":null,"errorMessage":"failed to marshal, message is %T, want proto.Message","messagePattern":"failed to marshal, message is %T, want proto\\.Message","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"encoding/protojson/protojson.go","lineNumber":36,"sourceCode":"\t\tEmitUnpopulated: true,\n\t}\n\t// UnmarshalOptions is a configurable JSON format parser.\n\tUnmarshalOptions = protojson.UnmarshalOptions{\n\t\tDiscardUnknown: true,\n\t}\n)\n\nfunc init() {\n\tencoding.RegisterCodec(codec{})\n}\n\n// codec is a Codec implementation with protojson.\ntype codec struct{}\n\nfunc (codec) Marshal(v any) ([]byte, error) {\n\tm, ok := v.(proto.Message)\n\tif !ok {\n\t\treturn nil, fmt.Errorf(\"failed to marshal, message is %T, want proto.Message\", v)\n\t}\n\treturn MarshalOptions.Marshal(m)\n}\n\nfunc (codec) Unmarshal(data []byte, v any) error {\n\tif len(data) == 0 {\n\t\treturn nil\n\t}\n\tm, ok := v.(proto.Message)\n\tif !ok {\n\t\treturn fmt.Errorf(\"failed to unmarshal, message is %T, want proto.Message\", v)\n\t}\n\treturn UnmarshalOptions.Unmarshal(data, m)\n}\n\nfunc (codec) Name() string {\n\treturn Name\n}","sourceCodeStart":18,"sourceCodeEnd":54,"githubUrl":"https://github.com/go-kratos/kratos/blob/668db92c2c001e9552594ba5a8aede8456af6d7e/encoding/protojson/protojson.go#L18-L54","documentation":"Codec error from Kratos' protojson encoding.RegisterCodec: Marshal was called with a value that does not implement proto.Message (google.golang.org/protobuf/proto). The codec is registered under the protojson name, so any framework path that marshals with it - HTTP response encoding, codec.Marshal, mid-client calls - requires a real protobuf-generated message, not a plain Go struct, map, or string. %T reports the concrete type that was passed.","triggerScenarios":"Returning a non-proto type (plain struct, map[string]any, string) from a Kratos HTTP handler whose response codec is protojson; passing a gogo/protobuf or golang/protobuf (v1 API) message, which does not implement the new proto.Message; configuring the route/service to use codec name 'protojson' while the handler returns JSON-built structs; client-side codec.Marshal(protojson.Name) over arbitrary values.","commonSituations":"Mixing JSON-first handlers into a proto-based service; migrating from gogo/golang-protobuf generated types to google.golang.org/protobuf and missing one message; wrapping proto messages in an envelope struct (e.g. {Data: msg}) that itself is not a proto message; using protojson codec by name string where a json codec was intended.","solutions":["Return the protobuf-generated message type itself (v2 API, google.golang.org/protobuf) from handlers/marshal calls","Remove non-proto envelope wrappers; put metadata into proto fields instead of wrapping the message in an anonymous struct","If you intended plain JSON, register/use the 'json' codec for that route instead of 'protojson'","For legacy gogo/v1 generated code, regenerate with the modern protoc-gen-go or provide a custom codec that accepts those types","Check the %T in the message to find which call site passed the wrong type"],"exampleFix":"// before: handler returns a plain struct under the protojson codec\ntype resp struct{ Msg string }\nreturn &resp{Msg: \"hi\"} // -> failed to marshal, message is *main.resp, want proto.Message\n\n// after: return the generated proto message\nreturn &v1.SayHelloResponse{Msg: \"hi\"}, nil","handlingStrategy":"type-guard","validationCode":"// Guard before marshaling with the protojson codec\nfunc marshalProtojson(v any) ([]byte, error) {\n\tif _, ok := v.(proto.Message); !ok {\n\t\treturn nil, fmt.Errorf(\"protojson codec requires proto.Message, got %T\", v)\n\t}\n\treturn protojson.Marshal(v.(proto.Message))\n}","typeGuard":"func isProtoMessage(v any) bool {\n\t_, ok := v.(proto.Message)\n\treturn ok\n}","tryCatchPattern":"data, err := codec.Marshal(v)\nif err != nil {\n\tif strings.Contains(err.Error(), \"want proto.Message\") {\n\t\t// wrong type at call site: return a developer-facing 500, never retry\n\t\tlog.Error(\"protojson misuse\", \"type\", fmt.Sprintf(\"%T\", v))\n\t\treturn errors.InternalServer(\"CODEC_MISUSE\", err.Error())\n\t}\n}","preventionTips":["Type handler returns as generated proto messages; compile-time check: func() proto.Message { return h() }","Use the 'json' codec (encoding/json) for routes that return plain structs","Avoid envelope wrappers around proto messages; add metadata as proto fields","During protobuf migrations, grep for legacy gogo/golang-protobuf types in handlers"],"tags":["go","kratos","protojson","codec","protobuf","http"],"backgroundTag":null,"analyzedSha":"668db92c2c001e9552594ba5a8aede8456af6d7e","analyzedAt":"2026-08-16T02:07:20.704Z","schemaVersion":2},"datasetVersion":"2026-08-16T03:17:38.424Z"}