[BUG] sanitize_tool_name returns an empty string for non-ASCII tool names, breaking function calling and tool prompts
Nobody has claimed this yet.
- Dominant language
- Python
- Stars
- 58.8k
- Forks
- 8.5k
- Avg merge
- 1d 15h
- Merged PRs (30d)
- 109
Description
Description
crewai.utilities.string_utils.sanitize_tool_name normalizes tool names for provider APIs by stripping everything outside [a-z0-9_]. When a tool name contains no ASCII letters or digits at all (for example a Chinese name like 搜索工具), the function returns the empty string "". That empty name flows unchanged into native function-calling payloads (convert_tools_to_openai_schema emits "function": {"name": ""}), which provider APIs reject because function/tool names must be non-empty and match ^[a-zA-Z0-9_-]{1,64}$. The resulting provider-side 400 error never mentions the real cause. A second non-ASCII-named tool in the same crew gets collision-renamed to "2", and the ReAct prompt lists the tools as ", ", so the text-tool-calling path cannot address them either. Tool names are unrestricted user-facing strings (BaseTool.name has no constraint), so international users hit this with perfectly valid tool definitions.
Steps to Reproduce
- Define a
BaseToolwhosenamehas no ASCII letters/digits, e.g.name: str = "搜索工具". - Call
convert_tools_to_openai_schema([tool])(or run a crew using native function calling with that tool). - Inspect the emitted schema:
"function": {"name": ""}. - With two such tools, the names become
["", "2"];get_tool_namesrenders", ".
Expected behavior
Every tool name sanitizes to a valid, unique, stable provider-safe name. A name with no ASCII content should get a deterministic fallback (the function already has one for over-length names: sha256(...)[:8]), never the empty string.
Screenshots/Code snippets
from crewai.utilities.string_utils import sanitize_tool_name
sanitize_tool_name("搜索工具") # -> '' (expected: a valid non-empty name)
sanitize_tool_name("抓取网页") # -> '' (identical output for a different name)
Schema emitted for two such tools: "name": "" and "name": "2".
Operating System: Windows 11 (reproduced on; the code path is platform-independent)
Python Version: 3.12 (reproduced on 3.12.7 and 3.13.12)
crewAI Version: 1.15.22 (also reproduced on main @ 3831e8b)
crewAI Tools Version: 1.15.22
Virtual Environment: Venv (uv)
Evidence
sanitize_tool_name("搜索工具") -> ''
function names in OpenAI schema: ["", "2"]
available_functions keys: ['', '2']
prompt tool list: ', '
Root cause: lib/crewai/src/crewai/utilities/string_utils.py — after the cleaning pipeline (NFKD → ASCII-drop → camelCase split → disallowed-char replacement → strip("_")), only the over-length branch has a deterministic hash fallback; the empty case returns "" unchecked.
Possible Solution
In sanitize_tool_name, after the cleaning steps, fall back to a deterministic name derived from the original input when the result is empty, e.g. f"tool_{sha256(original_name.encode())[:8]}" (hashing the original, not the empty string, keeps distinct non-ASCII names distinct). Happy to open a PR with this plus regression tests.
Additional context
This issue was found and written with AI assistance; please apply the llm-generated label per the contribution policy. Checked open issues/PRs on 2026-09-19 and found no existing report of this behavior.
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in lib/crewai/src/crewai/utilities/string_utils.py at sanitize_tool_name, then trace convert_tools_to_openai_schema and get_tool_names as described. Add regression coverage for non-ASCII-only names and verify the resulting names are non-empty, provider-safe, deterministic, and distinct for different inputs.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- python
- Domain
- ai, api
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 84/100