{"record":{"id":"9044fd640d107716","repo":"langchain-ai/langchain","slug":"runnable-without-name-for-tool-constructor","errorCode":null,"errorMessage":"Runnable without name for tool constructor","messagePattern":"Runnable without name for tool constructor","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"libs/core/langchain_core/tools/convert.py","lineNumber":364,"sourceCode":"                extras=extras,\n            )\n\n        return _tool_factory\n\n    if len(args) != 0:\n        # Triggered if a user attempts to use positional arguments that\n        # do not exist in the function signature\n        # e.g., @tool(\"name\", runnable, \"extra_arg\")\n        # Here, \"extra_arg\" is not a valid argument\n        msg = \"Too many arguments for tool decorator. A decorator \"\n        raise ValueError(msg)\n\n    if runnable is not None:\n        # tool is used as a function\n        # for instance tool_from_runnable = tool(\"name\", runnable)\n        if not name_or_callable:\n            msg = \"Runnable without name for tool constructor\"\n            raise ValueError(msg)\n        if not isinstance(name_or_callable, str):\n            msg = \"Name must be a string for tool constructor\"\n            raise ValueError(msg)\n        return _create_tool_factory(name_or_callable)(runnable)\n    if name_or_callable is not None:\n        if callable(name_or_callable) and hasattr(name_or_callable, \"__name__\"):\n            # Used as a decorator without parameters\n            # @tool\n            # def my_tool():\n            #    pass\n            return _create_tool_factory(name_or_callable.__name__)(name_or_callable)\n        if isinstance(name_or_callable, str):\n            # Used with a new name for the tool\n            # @tool(\"search\")\n            # def my_tool():\n            #    pass\n            #\n            # or","sourceCodeStart":346,"sourceCodeEnd":382,"githubUrl":"https://github.com/langchain-ai/langchain/blob/e32fa9a52eab3b61ad7a45399bfde59b3e580fc4/libs/core/langchain_core/tools/convert.py#L346-L382","documentation":"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).","triggerScenarios":"tool(runnable=my_runnable) with no name; tool('', runnable=my_runnable); tool(None, runnable=my_runnable).","commonSituations":"Dynamically generating tools from chains in a loop and forgetting the name; assuming the Runnable's own name is used automatically.","solutions":["Pass a name: tool('my_chain', runnable=my_runnable)","Or derive it: tool(my_runnable.get_name() or 'fallback_name', runnable=my_runnable)","Prefer my_runnable.as_tool(name=...) which handles naming itself"],"exampleFix":"# before\nt = tool(runnable=my_runnable)\n\n# after\nt = tool(\"summarizer\", runnable=my_runnable)\n# or\nt = my_runnable.as_tool(name=\"summarizer\")","handlingStrategy":"validation","validationCode":"def named_tool(runnable, name: str | None = None):\n    name = name or runnable.get_name() or \"runnable_tool\"\n    if not isinstance(name, str) or not name:\n        msg = f\"Invalid tool name: {name!r}\"\n        raise ValueError(msg)\n    return tool(name, runnable=runnable)","typeGuard":"def is_valid_tool_name(name: object) -> bool:\n    return isinstance(name, str) and bool(name.strip())","tryCatchPattern":"try:\n    t = tool(runnable=runnable)\nexcept ValueError as e:\n    if \"without name\" in str(e):\n        t = tool(runnable.get_name() or \"runnable_tool\", runnable=runnable)\n    else:\n        raise","preventionTips":["Prefer runnable.as_tool(name=...) which defaults naming sensibly","When generating tools in loops, assert the name variable is a non-empty string first","Keep a naming helper that falls back to the Runnable's own name"],"tags":["langchain","tools","runnable","api-misuse"],"backgroundTag":null,"analyzedSha":"e32fa9a52eab3b61ad7a45399bfde59b3e580fc4","analyzedAt":"2026-08-14T18:42:09.092Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}