micro/go-micro · error

unsupported Content-Type

Error message

unsupported Content-Type

What it means

The go-micro gRPC codec can only decode message bodies for a fixed set of content types: application/grpc+json, application/grpc+proto, and application/grpc. ReadBody decodes the length-prefixed frame from the connection and then switches on the codec's stored ContentType; any other content type falls through to this error, leaving the body undecoded.

Source

Thrown at codec/grpc/grpc.go:67

func (c *Codec) ReadBody(b interface{}) error {
	// no body
	if b == nil {
		return nil
	}

	_, buf, err := decode(c.Conn)
	if err != nil {
		return err
	}

	switch c.ContentType {
	case "application/grpc+json":
		return json.Unmarshal(buf, b)
	case "application/grpc+proto", "application/grpc":
		return proto.Unmarshal(buf, b.(proto.Message))
	}

	return errors.New("unsupported Content-Type")
}

func (c *Codec) Write(m *codec.Message, b interface{}) error {
	var buf []byte
	var err error

	if ct := m.Header["Content-Type"]; len(ct) > 0 {
		c.ContentType = ct
	}

	if ct := m.Header["content-type"]; len(ct) > 0 {
		c.ContentType = ct
	}

	switch m.Type {
	case codec.Request:
		parts := strings.Split(m.Endpoint, ".")
		m.Header[":method"] = "POST"

View on GitHub (pinned to 24529f1404)

Solutions

  1. Set the request/response content-type to exactly "application/grpc+proto", "application/grpc", or "application/grpc+json" on both client and server.
  2. Strip any media-type parameters (charset etc.) or normalize the content-type before it reaches the codec.
  3. If you need another format, register/use a codec that supports it instead of the gRPC codec.
  4. Align codec configuration between client and server so both negotiate the same content type.

Example fix

// before
req.Header.Set("Content-Type", "application/grpc+protobuf")
// after
req.Header.Set("Content-Type", "application/grpc+proto")
Defensive patterns

Strategy: validation

Validate before calling

ct := header.Get("Content-Type")
base := strings.TrimSpace(strings.Split(ct, ";")[0])
switch base {
case "application/grpc+json", "application/grpc+proto", "application/grpc":
    // ok
default:
    return fmt.Errorf("content-type %q not supported by grpc codec", ct)
}

Type guard

func supportedCT(ct string) bool {
    switch strings.TrimSpace(strings.Split(ct, ";")[0]) {
    case "application/grpc+json", "application/grpc+proto", "application/grpc":
        return true
    }
    return false
}

Prevention

When it happens

Trigger: ReadBody is called after ReadHeader stored a ContentType that is not one of the three supported values — e.g. a request with "content-type: application/grpc+protobuf" (misspelled), "text/plain", "application/json" (without the grpc prefix), or an empty content-type header on the incoming stream.

Common situations: A client library or proxy sends a content-type that differs slightly from what go-micro expects (e.g. application/grpc-web, or a charset suffix like "application/grpc+proto; charset=utf-8"); a custom codec was registered on one side but not the other; tests forget to set the content-type header.

Related errors


AI-assisted analysis of micro/go-micro@24529f1404 (2026-09-01). Data as JSON: /api/errors/ce488564f064929a. Report an issue: GitHub.