diff --git a/rpc/client.go b/rpc/client.go index b644a04c48..314612c1bb 100644 --- a/rpc/client.go +++ b/rpc/client.go @@ -364,7 +364,7 @@ func (c *Client) CallContext(ctx context.Context, result interface{}, method str resp := batchresp[0] switch { case resp.Error != nil: - return resp.Error + return resp.jsonErr() case len(resp.Result) == 0: return ErrNoResult default: @@ -449,7 +449,7 @@ func (c *Client) BatchCallContext(ctx context.Context, b []BatchElem) error { elem := &b[index] switch { case resp.Error != nil: - elem.Error = resp.Error + elem.Error = resp.jsonErr() case resp.Result == nil: elem.Error = ErrNoResult default: diff --git a/rpc/handler.go b/rpc/handler.go index 12828b3350..1a1a31f920 100644 --- a/rpc/handler.go +++ b/rpc/handler.go @@ -237,7 +237,7 @@ func (h *handler) handleBatch(msgs []*jsonrpcMessage) { resp := h.handleCallMsg(cp, msg) callBuffer.pushResponse(resp) if resp != nil && h.batchResponseMaxSize != 0 { - responseBytes += len(resp.Result) + responseBytes += len(resp.Result) + len(resp.Error) if responseBytes > h.batchResponseMaxSize { err := &internalServerError{errcodeResponseTooLarge, errMsgResponseTooLarge} callBuffer.respondWithError(cp.ctx, h.conn, err) @@ -415,7 +415,7 @@ func (h *handler) handleResponses(batch []*jsonrpcMessage, handleCall func(*json // the op.resp channel. if op.sub != nil { if msg.Error != nil { - op.err = msg.Error + op.err = msg.jsonErr() } else { op.err = json.Unmarshal(msg.Result, &op.sub.subid) if op.err == nil { @@ -481,9 +481,10 @@ func (h *handler) handleCallMsg(ctx *callProc, msg *jsonrpcMessage) *jsonrpcMess var logctx []any logctx = append(logctx, "reqid", idForLog{msg.ID}, "duration", time.Since(start)) if resp.Error != nil { - logctx = append(logctx, "err", resp.Error.Message) - if resp.Error.Data != nil { - logctx = append(logctx, "errdata", formatErrorData(resp.Error.Data)) + je := resp.jsonErr() + logctx = append(logctx, "err", je.Message) + if je.Data != nil { + logctx = append(logctx, "errdata", formatErrorData(je.Data)) } h.log.Warn("Served "+msg.Method, logctx...) } else { @@ -550,7 +551,7 @@ func (h *handler) handleCall(cp *callProc, msg *jsonrpcMessage) *jsonrpcMessage answer := h.runMethod(rctx, msg, callb, args) var rErr error if answer.Error != nil { - rErr = errors.New(answer.Error.Message) + rErr = errors.New(answer.jsonErr().Message) } rSpanEnd(&rErr) @@ -623,7 +624,7 @@ func (h *handler) runMethod(ctx context.Context, msg *jsonrpcMessage, callb *cal _, _, spanEnd := telemetry.StartSpanWithTracer(ctx, h.tracer(), "rpc.encodeJSONResponse", attributes...) response := msg.response(result) if response.Error != nil { - err = errors.New(response.Error.Message) + err = errors.New(response.jsonErr().Message) } spanEnd(&err) return response diff --git a/rpc/json.go b/rpc/json.go index 863998c56a..e74aadc695 100644 --- a/rpc/json.go +++ b/rpc/json.go @@ -60,7 +60,7 @@ type jsonrpcMessage struct { ID json.RawMessage `json:"id,omitempty"` Method string `json:"method,omitempty"` Params json.RawMessage `json:"params,omitempty"` - Error *jsonError `json:"error,omitempty"` + Error json.RawMessage `json:"error,omitempty"` Result json.RawMessage `json:"result,omitempty"` } @@ -108,6 +108,16 @@ func (msg *jsonrpcMessage) errorResponse(err error) *jsonrpcMessage { return resp } +// jsonErr decodes the Error field into a jsonError struct. +func (msg *jsonrpcMessage) jsonErr() *jsonError { + if msg.Error == nil { + return nil + } + je := new(jsonError) + json.Unmarshal(msg.Error, je) + return je +} + func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage { var ( enc []byte @@ -125,19 +135,18 @@ func (msg *jsonrpcMessage) response(result interface{}) *jsonrpcMessage { } func errorMessage(err error) *jsonrpcMessage { - msg := &jsonrpcMessage{Version: vsn, ID: null, Error: &jsonError{ + je := &jsonError{ Code: errcodeDefault, Message: err.Error(), - }} - ec, ok := err.(Error) - if ok { - msg.Error.Code = ec.ErrorCode() } - de, ok := err.(DataError) - if ok { - msg.Error.Data = de.ErrorData() + if ec, ok := err.(Error); ok { + je.Code = ec.ErrorCode() } - return msg + if de, ok := err.(DataError); ok { + je.Data = de.ErrorData() + } + enc, _ := json.Marshal(je) + return &jsonrpcMessage{Version: vsn, ID: null, Error: enc} } type jsonError struct { @@ -248,12 +257,8 @@ func writeMessage(w io.Writer, msg *jsonrpcMessage) error { buf = append(buf, msg.Params...) } if msg.Error != nil { - errBytes, err := json.Marshal(msg.Error) - if err != nil { - return err - } buf = append(buf, `,"error":`...) - buf = append(buf, errBytes...) + buf = append(buf, msg.Error...) } if msg.Result != nil { buf = append(buf, `,"result":`...) diff --git a/rpc/subscription_test.go b/rpc/subscription_test.go index 89b629f7c9..d1888c8a9a 100644 --- a/rpc/subscription_test.go +++ b/rpc/subscription_test.go @@ -220,7 +220,7 @@ func readAndValidateMessage(in *json.Decoder) (*subConfirmation, *subscriptionRe case msg.isResponse(): var c subConfirmation if msg.Error != nil { - return nil, nil, msg.Error + return nil, nil, msg.jsonErr() } else if err := json.Unmarshal(msg.Result, &c.subid); err != nil { return nil, nil, fmt.Errorf("invalid response: %v", err) } else {