From d765ba263ae999ca947f9e427a080e7b33194e84 Mon Sep 17 00:00:00 2001 From: BrianHotopp Date: Fri, 10 Apr 2026 01:33:20 -0400 Subject: [PATCH] [2.x] fix: Return JSON-RPC error for unknown server methods (#9002) The fallback ServerHandler silently dropped requests with unknown methods, violating the JSON-RPC spec. Clients like Metals that send unrecognized requests would never receive a response, potentially causing timeouts or hangs. Return a -32601 (Method not found) error response instead. --------- Co-authored-by: Claude Opus 4.6 (1M context) --- .../main/scala/sbt/internal/server/ServerHandler.scala | 5 ++++- server-test/src/test/scala/testpkg/ResponseTest.scala | 10 ++++++++++ 2 files changed, 14 insertions(+), 1 deletion(-) diff --git a/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala index 5356b710d..56046fa13 100644 --- a/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala +++ b/main-command/src/main/scala/sbt/internal/server/ServerHandler.scala @@ -30,7 +30,10 @@ object ServerHandler { lazy val fallback: ServerHandler = ServerHandler({ handler => ServerIntent( - onRequest = { case x => handler.log.debug(s"Unhandled request received: ${x.method}: $x") }, + onRequest = { case x => + handler.log.debug(s"Unhandled request received: ${x.method}: $x") + handler.jsonRpcRespondError(Some(x.id), -32601, s"Unknown method: ${x.method}") + }, onResponse = { case x => handler.log.debug(s"Unhandled response received") }, onNotification = { case x => handler.log.debug(s"Unhandled notification received: ${x.method}: $x") diff --git a/server-test/src/test/scala/testpkg/ResponseTest.scala b/server-test/src/test/scala/testpkg/ResponseTest.scala index 4f248fc01..48f914932 100644 --- a/server-test/src/test/scala/testpkg/ResponseTest.scala +++ b/server-test/src/test/scala/testpkg/ResponseTest.scala @@ -79,6 +79,16 @@ class ResponseTest extends AbstractServerTest { } } + test("unknown method returns error") { + val id = svr.session.nextId() + svr.session.sendJsonRpc(id, "build/foo", "{}").get + val response = svr.session.waitForResponseMsg(10.seconds, id).get + assert( + response.error.exists(_.code == -32601), + s"Expected method-not-found error, got: $response" + ) + } + private def neverReceiveResponse( duration: FiniteDuration )(predicate: sbt.internal.protocol.JsonRpcResponseMessage => Boolean): Unit =