{"record":{"id":"c6d8dbfc02aa46b9","repo":"huggingface/smolagents","slug":"invalid-tool-name-self-name-must-be-a-valid-p","errorCode":null,"errorMessage":"Invalid Tool name '{self.name}': must be a valid Python identifier and not a reserved keyword","messagePattern":"Invalid Tool name '(.+?)': must be a valid Python identifier and not a reserved keyword","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/smolagents/tools.py","lineNumber":168,"sourceCode":"        }\n        # Validate class attributes\n        for attr, expected_type in required_attributes.items():\n            attr_value = getattr(self, attr, None)\n            if attr_value is None:\n                raise TypeError(f\"You must set an attribute {attr}.\")\n            if not isinstance(attr_value, expected_type):\n                raise TypeError(\n                    f\"Attribute {attr} should have type {expected_type.__name__}, got {type(attr_value)} instead.\"\n                )\n\n        # Validate optional output_schema attribute\n        output_schema = getattr(self, \"output_schema\", None)\n        if output_schema is not None and not isinstance(output_schema, dict):\n            raise TypeError(f\"Attribute output_schema should have type dict, got {type(output_schema)} instead.\")\n\n        # - Validate name\n        if not is_valid_name(self.name):\n            raise Exception(\n                f\"Invalid Tool name '{self.name}': must be a valid Python identifier and not a reserved keyword\"\n            )\n        # Validate inputs\n        for input_name, input_content in self.inputs.items():\n            assert isinstance(input_content, dict), f\"Input '{input_name}' should be a dictionary.\"\n            assert \"type\" in input_content and \"description\" in input_content, (\n                f\"Input '{input_name}' should have keys 'type' and 'description', has only {list(input_content.keys())}.\"\n            )\n            # Get input_types as a list, whether from a string or list\n            if isinstance(input_content[\"type\"], str):\n                input_types = [input_content[\"type\"]]\n            elif isinstance(input_content[\"type\"], list):\n                input_types = input_content[\"type\"]\n                # Check if all elements are strings\n                if not all(isinstance(t, str) for t in input_types):\n                    raise TypeError(\n                        f\"Input '{input_name}': when type is a list, all elements must be strings, got {input_content['type']}\"\n                    )","sourceCodeStart":150,"sourceCodeEnd":186,"githubUrl":"https://github.com/huggingface/smolagents/blob/30bb1161095dbae2271e6bc3cc4c219cc3897a57/src/smolagents/tools.py#L150-L186","documentation":"smolagents validates that Tool.name is a valid Python identifier and not a reserved keyword, because the name is embedded in generated tool code sent to the LLM and executed by name. is_valid_name rejects names containing spaces/hyphens, starting with digits, using keywords like 'class'/'import', or otherwise unparseable identifiers.","triggerScenarios":"Defining a Tool with name = \"web search\", \"1tool\", \"class\", \"fetch-data\", or any string that fails str.isidentifier() or is a keyword; raised at instantiation in validate_arguments via new_init.","commonSituations":"Using human-readable names with spaces or hyphens; naming a tool after a Python keyword (import, lambda, class); auto-generating tool names from file names or API operation slugs that include '-' or leading digits.","solutions":["Rename to a valid snake_case identifier, e.g. 'web_search', 'fetch_data'","Check with 'name'.isidentifier() and keyword.iskeyword(name) before defining","When generating names from slugs, re.sub(r'\\W|^(?=\\d)', '_', slug) and strip leading underscores"],"exampleFix":"# before\nclass WebSearch(Tool):\n    name = \"web search\"\n\n# after\nclass WebSearch(Tool):\n    name = \"web_search\"","handlingStrategy":"validation","validationCode":"import keyword\n\ndef valid_tool_name(name: str) -> bool:\n    return name.isidentifier() and not keyword.iskeyword(name)","typeGuard":"def valid_tool_name(name: str) -> bool:\n    return isinstance(name, str) and name.isidentifier() and not keyword.iskeyword(name)","tryCatchPattern":"try:\n    MyTool()\nexcept Exception as e:\n    if \"Invalid Tool name\" in str(e):\n        MyTool.name = re.sub(r\"\\W\", \"_\", MyTool.name)\n        MyTool()","preventionTips":["Use snake_case names without spaces/hyphens/digits-first","Filter generated names through valid_tool_name before class creation","Avoid Python keywords as tool names"],"tags":["smolagents","tool","identifier","validation"],"backgroundTag":"invalid-identifier-name","analyzedSha":"30bb1161095dbae2271e6bc3cc4c219cc3897a57","analyzedAt":"2026-08-28T18:52:54.169Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}