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

  1. Read the original exception class and message from the ToolCallError text and fix the underlying cause
  2. Check serena's logs for the full traceback (logged with exc_info)
  3. 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

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


AI-assisted analysis of oraios/serena@7fcbca7e62 (2026-08-29). Data as JSON: /api/errors/afdb35e55b1d223a. Report an issue: GitHub.