From 30e2908240b06618c7f7ae193b39805c413b25bf Mon Sep 17 00:00:00 2001 From: Yufeng He <40085740+he-yufeng@users.noreply.github.com> Date: Thu, 28 May 2026 03:21:00 +0800 Subject: [PATCH] fix: preserve empty-method requests during decode --- internal/jsonrpc2/messages.go | 6 +++++- internal/jsonrpc2/wire_test.go | 17 +++++++++++++++++ 2 files changed, 22 insertions(+), 1 deletion(-) diff --git a/internal/jsonrpc2/messages.go b/internal/jsonrpc2/messages.go index 77c95313..e39a420e 100644 --- a/internal/jsonrpc2/messages.go +++ b/internal/jsonrpc2/messages.go @@ -183,7 +183,11 @@ func DecodeMessage(data []byte) (Message, error) { if err != nil { return nil, err } - if msg.Method != "" { + var fields map[string]json.RawMessage + if err := internaljson.Unmarshal(data, &fields); err != nil { + return nil, fmt.Errorf("unmarshaling jsonrpc message fields: %w", err) + } + if _, hasMethod := fields["method"]; hasMethod { // has a method, must be a call return &Request{ Method: msg.Method, diff --git a/internal/jsonrpc2/wire_test.go b/internal/jsonrpc2/wire_test.go index 6f576f08..ecdce15e 100644 --- a/internal/jsonrpc2/wire_test.go +++ b/internal/jsonrpc2/wire_test.go @@ -63,6 +63,23 @@ func TestWireMessage(t *testing.T) { } } +func TestDecodeMessageWithEmptyMethodIsRequest(t *testing.T) { + msg, err := jsonrpc2.DecodeMessage([]byte(`{"jsonrpc":"2.0","id":5,"method":"","params":{}}`)) + if err != nil { + t.Fatal(err) + } + req, ok := msg.(*jsonrpc2.Request) + if !ok { + t.Fatalf("DecodeMessage returned %T, want *jsonrpc2.Request", msg) + } + if req.Method != "" { + t.Fatalf("Request.Method = %q, want empty string", req.Method) + } + if got := req.ID.Raw(); got != int64(5) { + t.Fatalf("Request.ID = %v, want 5", got) + } +} + func newNotification(method string, params any) jsonrpc2.Message { msg, err := jsonrpc2.NewNotification(method, params) if err != nil {