Improve error reporting for LSP backend issues (LS initialisation)

* Include concrete information on failing language servers
* Explain how to access logs to get detailed information

Resolves #1112
This commit is contained in:
Dominik Jain authored and Dominik Jain committed 2026-03-01 18:43:55 +01:00
1 parent 6a798ef0c9
commit 827512b4d1
3 files changed
+46 -14

No files matched your search

+32 -11
View File
@@ -12,6 +12,7 @@ from typing import TYPE_CHECKING, Optional, TypeVar
from sensai.util import logging
from sensai.util.logging import LogTime
from sensai.util.string import TextBuilder
from interprompt.jinja_template import JinjaTemplate
from serena import serena_version
@@ -23,10 +24,11 @@ from serena.config.serena_config import (
NamedToolInclusionDefinition,
RegisteredProject,
SerenaConfig,
SerenaPaths,
ToolInclusionDefinition,
)
from serena.dashboard import SerenaDashboardAPI
from serena.ls_manager import LanguageServerManager
from serena.ls_manager import LanguageServerManager, LanguageServerManagerInitialisationError
from serena.project import Project
from serena.prompt_factory import SerenaPromptFactory
from serena.task_executor import TaskExecutor
@@ -252,6 +254,7 @@ class SerenaAgent:
# project-specific instances, which will be initialized upon project activation
self._active_project: Project | None = None
self._lsp_init_error: LanguageServerManagerInitialisationError | None = None
# determine registered project to be activated (if any)
registered_project_to_activate: RegisteredProject | None = (
@@ -473,13 +476,26 @@ class SerenaAgent:
def get_language_server_manager_or_raise(self) -> LanguageServerManager:
language_server_manager = self.get_language_server_manager()
if language_server_manager is None:
raise Exception(
"The language server manager is not initialized, indicating a problem during project activation. "
"Inform the user, telling them to inspect Serena's logs in order to determine the issue. "
"IMPORTANT: Wait for further instructions before you continue!"
msg = TextBuilder("The language server manager is not initialized, indicating a problem during project initialisation.")
if self._lsp_init_error is not None:
msg.with_text(str(self._lsp_init_error))
msg.with_text("For details, please check the logs. " + self.get_log_inspection_instructions())
msg.with_text(
"IMPORTANT: Stop, do not attempt workarounds. Inform the user and wait for further instructions before you continue!"
)
raise Exception(msg.build())
return language_server_manager
def get_log_inspection_instructions(self) -> str:
if self.serena_config.web_dashboard:
return f"Live logs can be inspected via the dashboard at {self.get_dashboard_url()}"
else:
log_path = SerenaPaths().last_returned_log_file_path
if log_path is not None:
return f"Find the current log file here: f{log_path}"
else:
return "Unfortunately, logs are not available. We recommend enabling the web dashboard/logging in general."
def get_context(self) -> SerenaAgentContext:
return self._context
@@ -816,12 +832,17 @@ class SerenaAgent:
ls_timeout = tool_timeout - 5 # the LS timeout is for a single call, it should be smaller than the tool timeout
# instantiate and start the necessary language servers
self.get_active_project_or_raise().create_language_server_manager(
log_level=self.serena_config.log_level,
ls_timeout=ls_timeout,
trace_lsp_communication=self.serena_config.trace_lsp_communication,
ls_specific_settings=self.serena_config.ls_specific_settings,
)
try:
self._lsp_init_error = None
self.get_active_project_or_raise().create_language_server_manager(
log_level=self.serena_config.log_level,
ls_timeout=ls_timeout,
trace_lsp_communication=self.serena_config.trace_lsp_communication,
ls_specific_settings=self.serena_config.ls_specific_settings,
)
except LanguageServerManagerInitialisationError as e:
self._lsp_init_error = e
raise
def add_language(self, language: Language) -> None:
"""
+7 -1
View File
@@ -85,6 +85,11 @@ class SerenaPaths:
"""
file containing the ID of the last read news snippet
"""
self.last_returned_log_file_path: str | None = None
"""
the path to the last log file returned by `get_next_log_file_path`. If this is not None, the logs
are currently being written to this file
"""
def get_next_log_file_path(self, prefix: str) -> str:
"""
@@ -93,7 +98,8 @@ class SerenaPaths:
"""
log_dir = os.path.join(self.serena_user_home_dir, "logs", datetime.now().strftime("%Y-%m-%d"))
os.makedirs(log_dir, exist_ok=True)
return os.path.join(log_dir, prefix + "_" + datetime_tag() + ".txt")
self.last_returned_log_file_path = os.path.join(log_dir, prefix + "_" + datetime_tag() + ".txt")
return self.last_returned_log_file_path
# TODO: Paths from constants.py should be moved here
+7 -2
View File
@@ -14,6 +14,11 @@ from solidlsp.settings import SolidLSPSettings
log = logging.getLogger(__name__)
class LanguageServerManagerInitialisationError(Exception):
def __init__(self, message: str):
super().__init__(message)
class LanguageServerFactory:
def __init__(
self,
@@ -122,14 +127,14 @@ class LanguageServerManager:
# If any server failed to start up, raise an exception and stop all started language servers.
# We intentionally fail fast here. The user's intention is to work with all the specified languages,
# so if any of them is not available, it is better to fail immediately and bring the failure to the
# so if any of them is not available, it is better to make symbolic tool calls fail, bringing the issue to the
# user's attention instead of silently continuing with a subset of the language servers and potentially
# causing suboptimal agent behaviour.
if exceptions:
for ls in language_servers.values():
ls.stop()
failure_messages = "\n".join([f"{lang.value}: {e}" for lang, e in exceptions.items()])
raise Exception(f"Failed to start language servers:\n{failure_messages}")
raise LanguageServerManagerInitialisationError(f"Failed to start {len(exceptions)} language server(s):\n{failure_messages}")
return LanguageServerManager(language_servers, factory)