langchain-ai/langchain · error · ValueError

Runnable without name for tool constructor

Error message

Runnable without name for tool constructor

What it means

When tool() is used as a function to wrap a Runnable (runnable=... keyword), the first positional argument must be the tool name. This error fires when runnable is provided but name_or_callable is empty (None or empty string).

Source

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

                extras=extras,
            )

        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

View on GitHub (pinned to e32fa9a52e)

Solutions

  1. Pass a name: tool('my_chain', runnable=my_runnable)
  2. Or derive it: tool(my_runnable.get_name() or 'fallback_name', runnable=my_runnable)
  3. Prefer my_runnable.as_tool(name=...) which handles naming itself

Example fix

# before
t = tool(runnable=my_runnable)

# after
t = tool("summarizer", runnable=my_runnable)
# or
t = my_runnable.as_tool(name="summarizer")
Defensive patterns

Strategy: validation

Validate before calling

def named_tool(runnable, name: str | None = None):
    name = name or runnable.get_name() or "runnable_tool"
    if not isinstance(name, str) or not name:
        msg = f"Invalid tool name: {name!r}"
        raise ValueError(msg)
    return tool(name, runnable=runnable)

Type guard

def is_valid_tool_name(name: object) -> bool:
    return isinstance(name, str) and bool(name.strip())

Try / catch

try:
    t = tool(runnable=runnable)
except ValueError as e:
    if "without name" in str(e):
        t = tool(runnable.get_name() or "runnable_tool", runnable=runnable)
    else:
        raise

Prevention

When it happens

Trigger: tool(runnable=my_runnable) with no name; tool('', runnable=my_runnable); tool(None, runnable=my_runnable).

Common situations: Dynamically generating tools from chains in a loop and forgetting the name; assuming the Runnable's own name is used automatically.

Related errors


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