underlying exception swallowed during codeAction
Describe the bug
We see metals stopping to provide completions and semantic highlighting in some cases and see this in the logs which looks like it swallows an underlying information, which would help to identify what causes the issue. As a first step we should reveal the CodeActionParams and the underlying information.
Mar 17, 2025 10:23:40 AM org.eclipse.lsp4j.jsonrpc.RemoteEndpoint fallbackResponseError
SEVERE: Internal error: An unexpected exception occurred while executing jsonrpc method public abstract java.util.concurrent.CompletableFuture scala.meta.metals.lsp.TextDocumentService.codeAction(org.eclipse.lsp4j.CodeActionParams)
java.lang.IllegalStateException: An unexpected exception occurred while executing jsonrpc method public abstract java.util.concurrent.CompletableFuture scala.meta.metals.lsp.TextDocumentService.codeAction(org.eclipse.lsp4j.CodeActionParams)
at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.lambda$recursiveFindRpcMethods$0(GenericEndpoint.java:73)
at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.request(GenericEndpoint.java:128)
at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleRequest(RemoteEndpoint.java:271)
at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:201)
at org.eclipse.lsp4j.jsonrpc.TracingMessageConsumer.consume(TracingMessageConsumer.java:119)
at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:185)
at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:97)
at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:114)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)
Expected behavior
No response
Operating system
None
Editor/Extension
VS Code
Version of Metals
v1.5.2
Extra context or search terms
No response
Thanks for reporting! Are there no other errors anywhere? Or anything in .metals/reports directory?
I think we would have noticed if there were related errors in other places, but I'll look again next time I see this.
Worth mentioning we also hit this with another method (but this time it was textDocument/didChange):
java.lang.IllegalStateException: An unexpected exception occurred while executing jsonrpc method public abstract java.util.concurrent.CompletableFuture scala.meta.metals.lsp.TextDocumentService.didChange(org.eclipse.lsp4j.DidChangeTextDocumentParams)
at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.lambda$recursiveFindRpcMethods$0(GenericEndpoint.java:73)
at org.eclipse.lsp4j.jsonrpc.services.GenericEndpoint.notify(GenericEndpoint.java:160)
at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.handleNotification(RemoteEndpoint.java:231)
at org.eclipse.lsp4j.jsonrpc.RemoteEndpoint.consume(RemoteEndpoint.java:198)
at org.eclipse.lsp4j.jsonrpc.TracingMessageConsumer.consume(TracingMessageConsumer.java:119)
at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.handleMessage(StreamMessageProducer.java:185)
at org.eclipse.lsp4j.jsonrpc.json.StreamMessageProducer.listen(StreamMessageProducer.java:97)
at org.eclipse.lsp4j.jsonrpc.json.ConcurrentMessageProcessor.run(ConcurrentMessageProcessor.java:114)
at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
at java.base/java.lang.Thread.run(Thread.java:840)
I'm pretty sure there again there was no underlying cause in that case as well.
Trying to walk through the code to reverse engineer how such an error log might be reached, I found:
lsp4jerror comes from here- in order for the stacktrace to point up to this location, the error would've had to occur while constructing the
CompletableFuture(not the execution of said future). That's totally something that might happen though, since Metals routinely has functions returningFuturewhere some initial logic happens synchronously in the function call. - this is where it gets trickier... for there not to be some underlying cause printed after the exception, the
InvocationTargetExceptionwould need to have itsgetCausebenull - ChatGPT seemed to think that there might be issues if the underlying exception was from a class that isn't present in the classloader doing the
invoke. That sounds potentially promising: it is totally possible for the error to be somewhere in Scala-version-specific presentation compiler instances and to reference some type that is only in that Scala compiler version (even more likely given this is another case where we're using 2.12.17 for the project, while the main Metals classloader would be loading Scala 2.13). That said, when I tried to verify this idea, I wasn't able to find anything and I'm starting to think it could just be a hallucination. 😄
I think what Chat GPT said might be true when the classloaders are unrelated, in this case the main classloader is the parent of the 2.12.17 classloader. I also tried throwing exceptions both in the normal code and behind the classloader and both times I get the correct cause. I can't seem to figure out when it wouldn't have the cause.
It seems almost impossible based on lsp4j code 🤔
Anyways, I also haven't seen this error since we fixed the thread safety issue around access to compiler issues, so priority isn't high. If/when it next occurs, I will be extra vigilant about double checking explicitly that there is no underlying cause (and collecting any other information)