run-llama/llama_index · error · ValueError

spec_functions must be of type: List[Union[str, Tuple[str, s

Error message

spec_functions must be of type: List[Union[str, Tuple[str, str]]]

What it means

BaseToolSpec.to_tool_list() validates each entry of spec_functions. Entries must be a plain string (function name) or a tuple of exactly two strings (sync function name, async function name). Anything else - ints, 3-tuples, single-element tuples, lists - raises this ValueError before any tool is built.

Source

Thrown at llama-index-core/llama_index/core/tools/tool_spec/base.py:95

            if isinstance(func_spec, str):
                func = getattr(self, func_spec)
                if inspect.iscoroutinefunction(func):
                    func_async = func
                else:
                    func_sync = func
                metadata = func_to_metadata_mapping.get(func_spec, None)
                if metadata is None:
                    metadata = self.get_metadata_from_fn_name(func_spec)
            elif isinstance(func_spec, tuple) and len(func_spec) == 2:
                func_sync = getattr(self, func_spec[0])
                func_async = getattr(self, func_spec[1])
                metadata = func_to_metadata_mapping.get(func_spec[0], None)
                if metadata is None:
                    metadata = func_to_metadata_mapping.get(func_spec[1], None)
                    if metadata is None:
                        metadata = self.get_metadata_from_fn_name(func_spec[0])
            else:
                raise ValueError(
                    "spec_functions must be of type: List[Union[str, Tuple[str, str]]]"
                )

            tool = FunctionTool.from_defaults(
                fn=func_sync,
                async_fn=func_async,
                tool_metadata=metadata,
            )
            tool_list.append(tool)
        return tool_list

    async def to_tool_list_async(
        self,
        spec_functions: Optional[List[SPEC_FUNCTION_TYPE]] = None,
        func_to_metadata_mapping: Optional[Dict[str, ToolMetadata]] = None,
    ) -> List[FunctionTool]:
        """Asynchronously convert a tool spec to a list of tools."""
        return await asyncio.to_thread(

View on GitHub (pinned to afd0fef371)

Solutions

  1. Normalize every entry to either 'fn_name' or ('sync_fn_name', 'async_fn_name').
  2. If you have function objects, use their __name__: spec_functions=[fn.__name__ for fn in fns].
  3. Check for trailing commas that turn a string into a 1-tuple: ('query',) is invalid.

Example fix

# before
spec_functions = ["search", ("sync_read",), "summarize"]
tools = spec.to_tool_list(spec_functions=spec_functions)

# after
spec_functions = ["search", ("sync_read", "async_read"), "summarize"]
tools = spec.to_tool_list(spec_functions=spec_functions)
Defensive patterns

Strategy: validation

Validate before calling

def validate_spec_functions(spec_functions):
    for entry in spec_functions:
        if isinstance(entry, str):
            continue
        if isinstance(entry, tuple) and len(entry) == 2 and all(isinstance(x, str) for x in entry):
            continue
        raise TypeError(f'Bad spec_functions entry: {entry!r}; use str or (sync, async) tuple')
    return spec_functions

Type guard

from typing import Union, Tuple, List

def is_valid_spec_functions(value) -> bool:
    return isinstance(value, list) and all(
        isinstance(e, str) or (isinstance(e, tuple) and len(e) == 2 and all(isinstance(p, str) for p in e))
        for e in value
    )

Try / catch

try:
    tools = spec.to_tool_list(spec_functions=spec_functions)
except ValueError as e:
    if 'spec_functions' in str(e):
        raise ValueError(f'Fix spec_functions shape: {spec_functions}') from e
    raise

Prevention

When it happens

Trigger: Calling to_tool_list() on a tool spec subclass while passing spec_functions=[...] (or overriding/spec_functions containing) entries like ('fn',), ['fn'], ('sync','async','extra'), or a non-string value.

Common situations: Hand-writing a custom ToolSpec and mistyping spec_functions; passing a list of function objects instead of names; copying an example that used 3-tuples from an older API.

Related errors


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/db308ad34b57aff1. Report an issue: GitHub.