grpc-ecosystem/grpc-gateway · error
unable to marshal non proto field
Error message
unable to marshal non proto field
What it means
ProtoMarshaller.Marshal only accepts values implementing the proto.Message interface; if the runtime.Marshaler receives anything else (string, struct, map, json.RawMessage, etc.) it returns "unable to marshal non proto field". This marshaller is meant for endpoints configured with the proto content type (application/proto), so passing a non-proto value to it is a programming/configuration error.
Source
Thrown at runtime/marshal_proto.go:22
"errors"
"io"
"google.golang.org/protobuf/proto"
)
// ProtoMarshaller is a Marshaller which marshals/unmarshals into/from serialize proto bytes
type ProtoMarshaller struct{}
// ContentType always returns "application/octet-stream".
func (*ProtoMarshaller) ContentType(_ interface{}) string {
return "application/octet-stream"
}
// Marshal marshals "value" into Proto
func (*ProtoMarshaller) Marshal(value interface{}) ([]byte, error) {
message, ok := value.(proto.Message)
if !ok {
return nil, errors.New("unable to marshal non proto field")
}
return proto.Marshal(message)
}
// Unmarshal unmarshals proto "data" into "value"
func (*ProtoMarshaller) Unmarshal(data []byte, value interface{}) error {
message, ok := value.(proto.Message)
if !ok {
return errors.New("unable to unmarshal non proto field")
}
return proto.Unmarshal(data, message)
}
// NewDecoder returns a Decoder which reads proto stream from "reader".
func (marshaller *ProtoMarshaller) NewDecoder(reader io.Reader) Decoder {
return DecoderFunc(func(value interface{}) error {
buffer, err := io.ReadAll(reader)
if err != nil {View on GitHub (pinned to a58a4436a3)
Solutions
- Ensure the value passed to Marshal implements proto.Message (use generated types like *pb.MyResponse).
- If returning JSON, use the JSONPb marshaller instead of ProtoMarshaller.
- When writing custom handlers, obtain the proto message first (e.g. from the generated handler or by constructing the pb type) before marshalling.
- Check mux/route configuration so the right marshaler is registered for the content type.
Example fix
// before
var resp map[string]interface{} = ...
data, err := protoMarshaller.Marshal(resp)
// after
resp := &pb.MyResponse{Name: "x"}
data, err := protoMarshaller.Marshal(resp) Defensive patterns
Strategy: type-guard
Validate before calling
if _, ok := v.(proto.Message); !ok {
return fmt.Errorf("%T does not implement proto.Message; use JSONPb for JSON output", v)
} Type guard
func isProtoMessage(v interface{}) bool {
_, ok := v.(proto.Message)
return ok
} Try / catch
data, err := marshaller.Marshal(value)
if err != nil {
if err.Error() == "unable to marshal non proto field" {
return fmt.Errorf("use JSONPb for non-proto values (%T)", value)
}
return err
} Prevention
- Only pass generated pb.* types to the proto marshaller.
- Register JSONPb for application/json and ProtoMarshaller for application/proto on the mux.
- In custom handlers, construct the proto message before marshalling the response.
When it happens
Trigger: Calling runtime.Marshaler.Marshal (or serving a route configured with runtime.ProtoMarshaller{}) with a value that does not implement proto.Message — e.g. passing an HTTP handler's struct, a map, or a pointer to a non-proto type into runtime.Marshal/ServeContent with the proto marshaller.
Common situations: Writing a custom HTTP handler that mixes the JSON marshaller and proto marshaller incorrectly; wrapping generated handlers with custom code that passes a plain struct; using runtime.ServeContent/HTTPBody with a non-message argument on a proto-configured server.
Related errors
- unable to unmarshal non proto field
- JSON structure did not match request type
- no target service defined in the file
- only primitive and enum types are allowed in repeated path p
- only primitive and well-known types are allowed in path para
AI-assisted analysis of grpc-ecosystem/grpc-gateway@a58a4436a3 (2026-09-02).
Data as JSON: /api/errors/f4d43977f2e132b5.
Report an issue: GitHub.