[2.x] fix : BSP compile returns StatusCode.Error on failure (#8104) (#8709)

- BuildServerProtocol: for Result.Inc(cause), return StatusCode.Error for any
  non-InterruptedException (was throwing for non-CompileFailed, causing JSON-RPC
  error instead of BspCompileResult with statusCode Error)
- BuildServerTest: add test 'buildTarget/compile - returns StatusCode.Error
  when compilation fails' (introduce compile error, compile via BSP, assert
  statusCode == Error)
This commit is contained in:
PandaMan 2026-02-07 11:01:49 -05:00 committed by GitHub
parent f6f00c1931
commit 95c6d42f8e
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
3 changed files with 45 additions and 4 deletions

1
.gitignore vendored
View File

@ -17,3 +17,4 @@ launcher-package/citest/freshly-baked
.vscode
sbt-launch.jar
local-temp
.jdk

View File

@ -23,7 +23,7 @@ import sbt.StandardMain.exchange
import sbt.internal.bsp.*
import sbt.internal.langserver.ErrorCodes
import sbt.internal.protocol.JsonRpcRequestMessage
import sbt.internal.util.{ Attributed, ErrorHandling }
import sbt.internal.util.{ Attributed, ErrorHandling, MessageOnlyException }
import sbt.internal.util.complete.{ Parser, Parsers }
import sbt.librarymanagement.CrossVersion.binaryScalaVersion
import sbt.librarymanagement.{ Configuration, ScalaArtifacts, UpdateReport }
@ -31,7 +31,6 @@ import sbt.std.TaskExtra
import sbt.util.Logger
import sjsonnew.shaded.scalajson.ast.unsafe.{ JNull, JValue }
import sjsonnew.support.scalajson.unsafe.{ CompactPrinter, Converter, Parser as JsonParser }
import xsbti.CompileFailed
import java.io.File
import java.nio.file.Paths
@ -879,9 +878,14 @@ object BuildServerProtocol {
case Result.Value(_) => StatusCode.Success
case Result.Inc(cause) =>
cause.getCause match {
case _: CompileFailed => StatusCode.Error
case _: InterruptedException => StatusCode.Cancelled
case err => throw cause
case _: MessageOnlyException =>
// Rethrow so task failure path sends JSON-RPC error (e.g. respondError project)
throw cause
case _ =>
// Return Error for any compile failure (CompileFailed or other Incomplete)
// so BSP returns a proper BspCompileResult instead of a JSON-RPC error (#8104)
StatusCode.Error
}
}
}

View File

@ -240,6 +240,42 @@ class BuildServerTest extends AbstractServerTest {
)
}
test("buildTarget/compile - returns StatusCode.Error when compilation fails") {
// Reproduces #8104: failed BSP compile must return BspCompileResult with statusCode Error,
// not a JSON-RPC error. Placed after diagnostics tests so shared message queue is not polluted.
val buildTarget = buildTargetUri("diagnostics", "Compile")
val mainFile = new File(svr.baseDirectory, "diagnostics/src/main/scala/Diagnostics.scala")
val original = IO.read(mainFile)
try {
IO.write(
mainFile,
"""|object Diagnostics {
| private val a: Int = ""
|}""".stripMargin
)
compile(buildTarget)
val res = svr.waitFor[BspCompileResult](30.seconds)
assert(
res.statusCode == StatusCode.Error,
s"expected StatusCode.Error, got ${res.statusCode}"
)
// Drain notifications from this failing compile so the next test does not consume them
def ourFailureNotification(s: String): Boolean =
(s.contains("build/taskStart") && s.contains("diagnostics")) ||
(s.contains("build/taskFinish") && s.contains("Compiled diagnostics") && s.contains(
"\"status\":2"
)) ||
(s.contains("build/publishDiagnostics") && s.contains("Diagnostics.scala") && s.contains(
"type mismatch"
))
svr.waitForString(30.seconds)(ourFailureNotification)
svr.waitForString(30.seconds)(ourFailureNotification)
svr.waitForString(30.seconds)(ourFailureNotification)
} finally {
IO.write(mainFile, original)
}
}
test("buildTarget/compile: Java diagnostics") {
val buildTarget = buildTargetUri("javaProj", "Compile")