huggingface/smolagents · error · Exception
Invalid Tool name '{self.name}': must be a valid Python iden
Error message
Invalid Tool name '{self.name}': must be a valid Python identifier and not a reserved keyword What it means
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.
Source
Thrown at src/smolagents/tools.py:168
}
# Validate class attributes
for attr, expected_type in required_attributes.items():
attr_value = getattr(self, attr, None)
if attr_value is None:
raise TypeError(f"You must set an attribute {attr}.")
if not isinstance(attr_value, expected_type):
raise TypeError(
f"Attribute {attr} should have type {expected_type.__name__}, got {type(attr_value)} instead."
)
# Validate optional output_schema attribute
output_schema = getattr(self, "output_schema", None)
if output_schema is not None and not isinstance(output_schema, dict):
raise TypeError(f"Attribute output_schema should have type dict, got {type(output_schema)} instead.")
# - Validate name
if not is_valid_name(self.name):
raise Exception(
f"Invalid Tool name '{self.name}': must be a valid Python identifier and not a reserved keyword"
)
# Validate inputs
for input_name, input_content in self.inputs.items():
assert isinstance(input_content, dict), f"Input '{input_name}' should be a dictionary."
assert "type" in input_content and "description" in input_content, (
f"Input '{input_name}' should have keys 'type' and 'description', has only {list(input_content.keys())}."
)
# Get input_types as a list, whether from a string or list
if isinstance(input_content["type"], str):
input_types = [input_content["type"]]
elif isinstance(input_content["type"], list):
input_types = input_content["type"]
# Check if all elements are strings
if not all(isinstance(t, str) for t in input_types):
raise TypeError(
f"Input '{input_name}': when type is a list, all elements must be strings, got {input_content['type']}"
)View on GitHub (pinned to 30bb116109)
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
Example fix
# before
class WebSearch(Tool):
name = "web search"
# after
class WebSearch(Tool):
name = "web_search" Defensive patterns
Strategy: validation
Validate before calling
import keyword
def valid_tool_name(name: str) -> bool:
return name.isidentifier() and not keyword.iskeyword(name) Type guard
def valid_tool_name(name: str) -> bool:
return isinstance(name, str) and name.isidentifier() and not keyword.iskeyword(name) Try / catch
try:
MyTool()
except Exception as e:
if "Invalid Tool name" in str(e):
MyTool.name = re.sub(r"\W", "_", MyTool.name)
MyTool() Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- You must set an attribute {attr}.
- Attribute {attr} should have type {expected_type.__name__},
- Input '{input_name}': types {invalid_types} must be one of {
- Check {check_function.__name__} failed with error: {e}
- {e}
AI-assisted analysis of huggingface/smolagents@30bb116109 (2026-08-28).
Data as JSON: /api/errors/c6d8dbfc02aa46b9.
Report an issue: GitHub.