oraios/serena · error · ToolCallError
{e.__class__.__name__}: {e}
Error message
{e.__class__.__name__}: {e} What it means
The tool-dispatch wrapper task() catches any exception from apply() that is not already a ToolCallError, logs it, and re-raises it as ToolCallError with the message '<ExceptionClass>: <original message>'. This normalizes all unexpected tool failures into a single error type surfaced to the agent/client.
Source
Thrown at src/serena/tools/tools_base.py:407
self.agent.get_language_server_manager_or_raise().restart_language_server(affected_language)
result = apply_fn(**apply_kwargs)
else:
log.error(
f"Language server terminated while executing tool ({e}), but affected language is unknown. Not retrying."
)
raise
else:
raise
# record tool usage
self.agent.record_tool_usage(apply_kwargs, result, self)
except ToolCallError:
raise
except Exception as e:
msg = f"{e.__class__.__name__}: {e}"
log.error(msg, exc_info=e)
raise ToolCallError(msg)
if log_call:
log.info(f"Result: {result}")
try:
ls_manager = self.agent.get_language_server_manager()
if ls_manager is not None:
ls_manager.save_all_caches()
except Exception as e:
log.error(f"Error saving language server cache: {e}")
return result
# execute the tool in the agent's task executor, with timeout
# (task timeout bounds task execution in the dispatcher once it runs, result timeout limits the time we wait)
tool_call_error: ToolCallError
timeout = self.agent.serena_config.tool_timeout
try:View on GitHub (pinned to 7fcbca7e62)
Solutions
- Read the original exception class and message from the ToolCallError text and fix the underlying cause
- Check serena's logs for the full traceback (logged with exc_info)
- Catch ToolCallError at the call site and inspect its message to branch on root causes
Example fix
// before
try:
tool.apply(...)
except FileNotFoundError: # never reached — already wrapped
...
// after
try:
tool.apply(...)
except ToolCallError as e:
if 'FileNotFoundError' in str(e):
fix_path_and_retry()
else:
raise Defensive patterns
Strategy: try-catch
Validate before calling
# no pre-call check possible; this is the dispatcher's catch-all wrapper. # Validate inputs of the underlying apply() yourself: assert (Path(project_root) / relative_path).exists(), 'bad path' assert max_answer_chars == -1 or max_answer_chars > 0
Try / catch
try:
result = tool.apply(...)
except ToolCallError as e:
original_class, _, original_msg = str(e).partition(': ')
log.warning('tool failed: %s %s', original_class, original_msg)
handle(original_class, original_msg) Prevention
- Always catch ToolCallError, not the original exception types — they are wrapped
- Read logs for the full original traceback (logged with exc_info)
- Parse the '<Class>: <msg>' prefix to branch on root causes
- Validate tool arguments yourself before calling apply() to avoid the wrapper entirely
When it happens
Trigger: Any unhandled exception inside a tool's apply() — e.g. the ValueErrors/FileNotFoundError above, encoding errors, network/LSP failures — bubbles up through task() and is wrapped.
Common situations: Debugging why a tool call failed: the visible ToolCallError is a wrapper; the log (log.error with exc_info) holds the original traceback; wrapping can double-prefix messages on nested task() calls.
Related errors
- Tool '{self.get_name_from_cls()}' is not active. Active tool
- No active project. Ask the user to provide the project path
- This method must be overridden for each subclass
- Context file not found: {path.resolve()}
- Cannot use both fixed_tools and excluded_tools/included_opti
AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29).
Data as JSON: /api/errors/afdb35e55b1d223a.
Report an issue: GitHub.