BerriAI/litellm · error · ValueError
allowed_callers must be a list of strings
Error message
allowed_callers must be a list of strings
What it means
For tools that support it (not tool-search/computer tools), a top-level 'allowed_callers' must be a list whose every element is a string. Anything else — a string, a dict, a list containing non-strings — raises this ValueError during transformation, before the API call.
Source
Thrown at litellm/llms/anthropic/chat/transformation.py:825
returned_tool["defer_loading"] = _defer_loading_function
## check if allowed_callers is set in the tool
_allowed_callers: Final = tool.get("allowed_callers", None)
_allowed_callers_function: Final = tool.get("function", {}).get("allowed_callers", None)
if returned_tool is not None:
# Only set allowed_callers on tools that support it (not tool search tools or computer tools)
tool_type = returned_tool.get("type", "")
if tool_type not in (
"tool_search_tool_regex_20251119",
"tool_search_tool_bm25_20251119",
"computer_20241022",
"computer_20250124",
):
if _allowed_callers is not None:
if not isinstance(_allowed_callers, list) or not all(
isinstance(item, str) for item in _allowed_callers
):
raise ValueError("allowed_callers must be a list of strings")
returned_tool["allowed_callers"] = _allowed_callers
elif _allowed_callers_function is not None:
if not isinstance(_allowed_callers_function, list) or not all(
isinstance(item, str) for item in _allowed_callers_function
):
raise ValueError("allowed_callers must be a list of strings")
returned_tool["allowed_callers"] = _allowed_callers_function
## check if input_examples is set in the tool
_input_examples: Final = tool.get("input_examples", None)
_input_examples_function: Final = tool.get("function", {}).get("input_examples", None)
if returned_tool is not None:
# Only set input_examples on user-defined tools (type "custom" or no type)
tool_type = returned_tool.get("type", "")
if tool_type == "custom" or (tool_type == "" and "name" in returned_tool):
if _input_examples is not None and isinstance(_input_examples, list):
returned_tool["input_examples"] = _input_examples
elif _input_examples_function is not None and isinstance(_input_examples_function, list):View on GitHub (pinned to 6c2dcb801b)
Solutions
- Wrap single values in a list: ['user@org'] and ensure every entry is a string.
- Convert numeric IDs: [str(c) for c in allowed_callers].
- Validate the shape at config-load time (list of str) with a JSON schema or small helper.
Example fix
# before
{"type": "custom", "name": "f", "allowed_callers": "alice"}
# after
{"type": "custom", "name": "f", "allowed_callers": ["alice"]} Defensive patterns
Strategy: validation
Validate before calling
ac = tool.get("allowed_callers")
if isinstance(ac, str):
tool["allowed_callers"] = [ac]
elif isinstance(ac, list):
tool["allowed_callers"] = [str(c) for c in ac] Type guard
def allowed_callers_ok(tool) -> bool:
ac = tool.get("allowed_callers")
return ac is None or (isinstance(ac, list) and all(isinstance(x, str) for x in ac)) Prevention
- Always wrap a single caller in a list in configs.
- str()-cast user IDs before adding them to allowed_callers.
When it happens
Trigger: Passing allowed_callers: 'user@org' (bare string instead of list), allowed_callers: ['a', 123], or a dict of caller permissions; typical when configuring MCP-style tools with caller restrictions.
Common situations: Config files where a single value is written as a scalar instead of a one-element list; caller lists built from user IDs that are ints; merging configs that silently changes the shape.
Related errors
- defer_loading must be a boolean
- Missing required parameter: parameters
- Missing required parameter: display_width_px or display_heig
- Missing required parameter: name
- Tool search tool must have a valid name
AI-assisted analysis of BerriAI/litellm@6c2dcb801b (2026-08-15).
Data as JSON: /api/errors/c58713a7a7113f49.
Report an issue: GitHub.