{"record":{"id":"bd67eabe57200168","repo":"jd-opensource/joyagent-jdgenie","slug":"error-bd67ea","errorCode":null,"errorMessage":"工具名称不能为空且必须是字符串类型","messagePattern":"工具名称不能为空且必须是字符串类型","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"genie-client/app/client.py","lineNumber":313,"sourceCode":"\n    async def call_tool(self, name: str, arguments: Optional[Dict[str, Any]] = None) -> Any:\n        \"\"\"\n        调用指定的工具\n\n        Args:\n            name: 工具名称\n            arguments: 工具参数字典，默认为空字典\n\n        Returns:\n            Any: 工具执行结果\n\n        Raises:\n            ValueError: 当工具名称无效时抛出\n            Exception: 当工具调用失败时抛出异常\n        \"\"\"\n        # 参数验证\n        if not name or not isinstance(name, str):\n            raise ValueError(\"工具名称不能为空且必须是字符串类型\")\n\n        if arguments is None:\n            arguments = {}\n        elif not isinstance(arguments, dict):\n            raise ValueError(\"工具参数必须是字典类型\")\n\n        try:\n            async with self._sse_connection() as session:\n                logger.info(f\"正在调用工具 '{name}'，参数: {arguments}\")\n\n                # 调用工具\n                response = await session.call_tool(name=name, arguments=arguments)\n\n                logger.info(f\"工具 '{name}' 执行成功\")\n                logger.debug(f\"工具 '{name}' 返回结果类型: {type(response).__name__}\")\n\n                return response\n","sourceCodeStart":295,"sourceCodeEnd":331,"githubUrl":"https://github.com/jd-opensource/joyagent-jdgenie/blob/2417e0b8b636d941ad5fb14c59b20dddfef5375d/genie-client/app/client.py#L295-L331","documentation":"Input validation at the start of the MCP client's call_tool: the tool name must be a non-empty string because it is the key used to look up the tool on the server. A None or non-string name would fail the RPC or cause a confusing lookup error downstream, so a ValueError is raised up front; fires when callers pass an invalid tool name (e.g. an unset variable) instead of a real tool identifier.","triggerScenarios":"`client.call_tool(None)`, `client.call_tool(\"\")`, or passing a non-string (int, Tool object, bytes) as the tool name.","commonSituations":"Tool name sourced from a config file or an upstream list where an entry is missing/empty; passing a tool object instead of its `name` field; YAML/JSON key mismatch yielding None.","solutions":["Ensure the tool name is a non-empty string before calling call_tool","If iterating a tool list, pass `tool.name`, not the tool object","Validate config-sourced names after loading and fail fast with a clear message"],"exampleFix":"// before\nawait client.call_tool(config[\"tool\"].id)\n// after\nawait client.call_tool(config[\"tool\"][\"name\"])","handlingStrategy":"validation","validationCode":"if not isinstance(tool_name, str) or not tool_name:\n    raise ValueError(f\"Invalid tool name: {tool_name!r}\")","typeGuard":"def is_valid_tool_name(name) -> bool:\n    return isinstance(name, str) and bool(name.strip())","tryCatchPattern":"try:\n    result = await client.call_tool(name, args)\nexcept ValueError as e:\n    logger.error(f\"bad tool arguments: {e}\")\n    raise","preventionTips":["Validate config-sourced tool names right after loading config","Pass `tool.name`, never a tool object, when iterating tool lists","Add a schema check on tool-name fields in your own data models"],"tags":["validation","arguments"],"backgroundTag":"missing-required-argument","analyzedSha":"2417e0b8b636d941ad5fb14c59b20dddfef5375d","analyzedAt":"2026-09-08T11:28:19.414Z","contentChangedAt":"2026-09-08T11:28:19.414Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}