langchain-ai/langchain · error · ValueError

Name must be a string for tool constructor

Error message

Name must be a string for tool constructor

What it means

In tool(name_or_callable, runnable=...) function-style usage, the first argument must be a string naming the tool. This raises when something other than a str (e.g. the function itself or a Runnable) was passed positionally together with runnable=.

Source

Thrown at libs/core/langchain_core/tools/convert.py:367

        return _tool_factory

    if len(args) != 0:
        # Triggered if a user attempts to use positional arguments that
        # do not exist in the function signature
        # e.g., @tool("name", runnable, "extra_arg")
        # Here, "extra_arg" is not a valid argument
        msg = "Too many arguments for tool decorator. A decorator "
        raise ValueError(msg)

    if runnable is not None:
        # tool is used as a function
        # for instance tool_from_runnable = tool("name", runnable)
        if not name_or_callable:
            msg = "Runnable without name for tool constructor"
            raise ValueError(msg)
        if not isinstance(name_or_callable, str):
            msg = "Name must be a string for tool constructor"
            raise ValueError(msg)
        return _create_tool_factory(name_or_callable)(runnable)
    if name_or_callable is not None:
        if callable(name_or_callable) and hasattr(name_or_callable, "__name__"):
            # Used as a decorator without parameters
            # @tool
            # def my_tool():
            #    pass
            return _create_tool_factory(name_or_callable.__name__)(name_or_callable)
        if isinstance(name_or_callable, str):
            # Used with a new name for the tool
            # @tool("search")
            # def my_tool():
            #    pass
            #
            # or
            #
            # @tool("search", parse_docstring=True)
            # def my_tool():

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Use only a string name with runnable=: tool('name', runnable=runnable)
  2. To wrap a function, use the decorator form or call the factory: tool('name')(my_func)
  3. To compose runnables, pipe them first (a | b) and then convert the composed chain

Example fix

# before
t = tool(my_func, runnable=my_runnable)

# after
t = tool("my_tool", runnable=my_runnable | my_func)
Defensive patterns

Strategy: type-guard

Validate before calling

def tool_from_runnable(runnable, name):
    if not isinstance(name, str):
        msg = f"name must be str, got {type(name).__name__}"
        raise TypeError(msg)
    return tool(name, runnable=runnable)

Type guard

def is_str_name(x: object) -> bool:
    return isinstance(x, str) and bool(x)

Try / catch

try:
    t = tool(first_arg, runnable=runnable)
except ValueError as e:
    if "Name must be a string" in str(e):
        t = tool(str(first_arg), runnable=runnable)
    else:
        raise

Prevention

When it happens

Trigger: tool(my_func, runnable=my_runnable); tool(my_runnable, runnable=my_runnable); any non-string first argument combined with the runnable keyword.

Common situations: Mixing decorator-style and function-style usage in one call; passing both a callable and a runnable expecting them to be composed.

Related errors


AI-assisted analysis of langchain-ai/langchain@e32fa9a52e (2026-08-14). Data as JSON: /api/errors/008989a7edde9867. Report an issue: GitHub.