micro/go-micro · error

invalid error %v

Error message

invalid error %v

What it means

The JSON-RPC client's ReadHeader parses the response envelope; the JSON-RPC spec allows the 'error' member to be any JSON value, but this codec only supports string errors. When resp.Error is present but not a JSON string (e.g. an object like {code, message, data} per the JSON-RPC 2.0 spec), ReadHeader returns 'invalid error' and the call fails instead of surfacing the server's error text.

Source

Thrown at codec/jsonrpc/client.go:80

}

func (c *clientCodec) ReadHeader(m *codec.Message) error {
	c.resp.reset()
	if err := c.dec.Decode(&c.resp); err != nil {
		return err
	}

	c.Lock()
	m.Method = c.pending[c.resp.ID]
	delete(c.pending, c.resp.ID)
	c.Unlock()

	m.Error = ""
	m.Id = fmt.Sprintf("%v", c.resp.ID)
	if c.resp.Error != nil {
		x, ok := c.resp.Error.(string)
		if !ok {
			return fmt.Errorf("invalid error %v", c.resp.Error)
		}
		if x == "" {
			x = "unspecified error"
		}
		m.Error = x
	}
	return nil
}

func (c *clientCodec) ReadBody(x interface{}) error {
	if x == nil || c.resp.Result == nil {
		return nil
	}
	return json.Unmarshal(*c.resp.Result, x)
}

func (c *clientCodec) Close() error {
	return c.c.Close()

View on GitHub (pinned to 24529f1404)

Solutions

  1. Make the server return its error as a plain string in the 'error' field.
  2. Patch/extend the client to handle object errors (extract 'message' from the object) instead of erroring.
  3. Use a different transport (e.g. grpc or the micro json codec) that matches the server's error format.
  4. Log the raw response to confirm the error shape before changing code.

Example fix

// before (server)
return nil, &jsonrpcError{Code: -32600, Message: "bad request"}
// after
return nil, errors.New("bad request")
Defensive patterns

Strategy: type-guard

Validate before calling

// Server side: only send string errors so this client can parse them.
// Client side: inspect the raw envelope before decoding if possible.
func errIsString(raw json.RawMessage) bool {
	return len(raw) > 0 && raw[0] == '"'
}

Type guard

func errAsString(v interface{}) (string, bool) {
	s, ok := v.(string)
	return s, ok
}

Try / catch

if err := client.Call(ctx, req, resp); err != nil {
	if strings.HasPrefix(err.Error(), "invalid error") {
		// server sent a non-string JSON-RPC error object; inspect raw response
	}
	return err
}

Prevention

When it happens

Trigger: Calling a JSON-RPC server that returns a structured error object (JSON-RPC 2.0 style {code:-32600,...}) while this client expects resp.Error to be a plain string.

Common situations: Interoperating with standard JSON-RPC 2.0 servers (Go net/rpc jsonrpc or third-party) that encode errors as objects; proxying requests to services using the official spec; version mismatch between client expectations and server implementation.

Related errors


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