{"record":{"id":"1c729927c52a26a3","repo":"oraios/serena","slug":"e-get-error-message","errorCode":null,"errorMessage":"e.get_error_message()","messagePattern":"e\\.get_error_message\\(\\)","errorType":"exception","errorClass":"ToolError","httpStatus":null,"severity":"warning","filePath":"src/serena/mcp.py","lineNumber":101,"sourceCode":"        if docstring.returns and (docstring_returns_descr := docstring.returns.description):\n            # Only add a space before \"Returns\" if func_doc is not empty\n            prefix = \" \" if func_doc else \"\"\n            func_doc = f\"{func_doc}{prefix}Returns {docstring_returns_descr.strip().strip('.')}.\"\n\n        # Parse the parameter descriptions from the docstring and add pass its description\n        # to the parameter schema.\n        docstring_params = {param.arg_name: param for param in docstring.params}\n        parameters_properties: dict[str, dict[str, Any]] = parameters[\"properties\"]\n        for parameter, properties in parameters_properties.items():\n            if (param_doc := docstring_params.get(parameter)) and param_doc.description:\n                param_desc = f\"{param_doc.description.strip().strip('.') + '.'}\"\n                properties[\"description\"] = param_desc[0].upper() + param_desc[1:]\n\n        def execute_fn(**kwargs) -> str:\n            try:\n                return tool.apply_ex(log_call=True, catch_exceptions=False, **kwargs)\n            except ToolCallError as e:\n                raise ToolError(e.get_error_message()) from e\n\n        # Generate human-readable title from snake_case tool name\n        tool_title = \" \".join(word.capitalize() for word in func_name.split(\"_\"))\n\n        # Create annotations with appropriate hints based on tool capabilities\n        can_edit = tool.can_edit()\n        annotations = ToolAnnotations(\n            title=tool_title,\n            readOnlyHint=not can_edit,\n            destructiveHint=can_edit,\n        )\n\n        super().__init__(\n            fn=execute_fn,\n            name=func_name,\n            description=func_doc,\n            parameters=parameters,\n            fn_metadata=func_arg_metadata,","sourceCodeStart":83,"sourceCodeEnd":119,"githubUrl":"https://github.com/oraios/serena/blob/7fcbca7e62555ec2287ddb2f083caee805848ea6/src/serena/mcp.py#L83-L119","documentation":"Serena's MCP adapter wraps each tool call; when tool.apply_ex raises ToolCallError (with catch_exceptions=False), the adapter converts it to an MCP ToolError whose message is the tool's user-facing error text via e.get_error_message(). This is the standard channel for surfacing tool-level failures (bad arguments, file not found, etc.) to the MCP client.","triggerScenarios":"Any MCP tool execution whose underlying apply_ex raises ToolCallError — e.g. find_symbol on a missing symbol, read/write of a nonexistent path, invalid regex in search tools, tool-specific precondition failures.","commonSituations":"Client passes a nonexistent file path or symbol name; timeout/no-match conditions raised as ToolCallError; caller-side confusion when the LLM client shows a generic 'tool error' containing the real message.","solutions":["Read the ToolError message — it contains the tool's actual failure description","Validate inputs (paths exist, symbol name format, regex compiles) before invoking the tool","Catch tool errors on the MCP client side and retry with corrected arguments","If the message is unclear, run the tool with log_level=debug to get the underlying stack trace"],"exampleFix":"// before (client passes bad path)\n{\"tool\": \"read_file\", \"arguments\": {\"relative_path\": \"nope.txt\"}}\n// after\n{\"tool\": \"read_file\", \"arguments\": {\"relative_path\": \"src/nope.txt\"}}\n// or guard on client side\nif os.path.isfile(os.path.join(root, rel_path)):\n    call_tool(\"read_file\", {\"relative_path\": rel_path})","handlingStrategy":"try-catch","validationCode":"from pathlib import Path\nif not (Path(project_root) / relative_path).exists():\n    raise FileNotFoundError(relative_path)","typeGuard":null,"tryCatchPattern":"try:\n    result = call_mcp_tool(\"read_file\", {\"relative_path\": rel})\nexcept ToolError as e:\n    logger.warning(\"tool failed: %s\", e)\n    result = retry_with_corrected_args(e)  # message states the actual problem","preventionTips":["Validate tool arguments (paths, symbol names, regexes) client-side","Parse the ToolError message — it contains the tool's own failure description","Retry with corrected arguments rather than treating as fatal","Enable debug logging when messages are ambiguous"],"tags":["mcp","tool-error","error-propagation"],"backgroundTag":"tool-call-failed","analyzedSha":"7fcbca7e62555ec2287ddb2f083caee805848ea6","analyzedAt":"2026-08-29T00:04:09.619Z","schemaVersion":2},"datasetVersion":"2026-08-29T02:17:18.158Z"}