{"record":{"id":"4bcd146fa5808b45","repo":"microsoft/autogen","slug":"the-provided-langchain-tool-name-does-not-have","errorCode":null,"errorMessage":"The provided LangChain tool '{name}' does not have a callable 'func' or '_run' method.","messagePattern":"The provided LangChain tool '(.+?)' does not have a callable 'func' or '_run' method\\.","errorType":"exception","errorClass":"AttributeError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-ext/src/autogen_ext/tools/langchain/_langchain_adapter.py","lineNumber":159,"sourceCode":"                asyncio.run(main())\n\n    \"\"\"\n\n    def __init__(self, langchain_tool: LangChainTool):\n        self._langchain_tool: LangChainTool = langchain_tool\n\n        # Extract name and description\n        name = self._langchain_tool.name\n        description = self._langchain_tool.description or \"\"\n\n        # Determine the callable method\n        if hasattr(self._langchain_tool, \"func\") and callable(self._langchain_tool.func):  # type: ignore\n            assert self._langchain_tool.func is not None  # type: ignore\n            self._callable: Callable[..., Any] = self._langchain_tool.func  # type: ignore\n        elif hasattr(self._langchain_tool, \"_run\") and callable(self._langchain_tool._run):  # type: ignore\n            self._callable: Callable[..., Any] = self._langchain_tool._run  # type: ignore\n        else:\n            raise AttributeError(\n                f\"The provided LangChain tool '{name}' does not have a callable 'func' or '_run' method.\"\n            )\n\n        # Determine args_type\n        if self._langchain_tool.args_schema:  # pyright: ignore\n            args_type = self._langchain_tool.args_schema  # pyright: ignore\n        else:\n            # Infer args_type from the callable's signature\n            sig = inspect.signature(cast(Callable[..., Any], self._callable))  # type: ignore\n            fields = {\n                k: (v.annotation, Field(...))\n                for k, v in sig.parameters.items()\n                if k != \"self\" and v.kind not in (inspect.Parameter.VAR_POSITIONAL, inspect.Parameter.VAR_KEYWORD)\n            }\n            args_type = create_model(f\"{name}Args\", **fields)  # type: ignore\n            # Note: type ignore is used due to a LangChain typing limitation\n\n        # Ensure args_type is a subclass of BaseModel","sourceCodeStart":141,"sourceCodeEnd":177,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-ext/src/autogen_ext/tools/langchain/_langchain_adapter.py#L141-L177","documentation":"LangChainToolAdapter.__init__ inspects the wrapped LangChain tool for a callable 'func' attribute (StructuredTool-style) or '_run' method (BaseTool subclass). If neither is present/callable, AttributeError is raised with the tool's name. This typically happens with tools whose executable is exposed differently (e.g. coroutine-only StructuredTool) or tools that are pure declaration objects.","triggerScenarios":"Passing a LangChain tool to LangChainToolAdapter where tool.func is None/absent and tool._run is not callable — e.g. a StructuredTool built with only coroutine=..., since StructuredTool sets func to a placeholder raising NotImplementedError that may not be flagged callable-but-usable, or a mock/Pydantic model mistaken for a tool.","commonSituations":"Wrapping an async-only StructuredTool created via StructuredTool(coroutine=async_fn) in older langchain versions; passing a tool schema/dict from an agent framework rather than an actual tool instance; passing @tool-decorated object from an incompatible langchain major version where attributes moved; passing Mock objects in tests.","solutions":["Create the tool so a callable func exists: use @tool decorator on a sync function, or StructuredTool.from_function(fn).","For async functions, decorate them with @tool anyway (the decorator wraps them and provides a runnable path), rather than hand-constructing StructuredTool with only coroutine.","Check the LangChain version compatibility with the autogen-ext langchain extra; upgrade/downgrade langchain to a supported major version.","Verify the object really is a BaseTool instance before adapting: isinstance(tool, BaseTool)."],"exampleFix":"# before\nfrom langchain_core.tools import StructuredTool\n\ntool = StructuredTool(\n    name=\"fetch\",\n    description=\"fetch data\",\n    coroutine=async_fetch,   # func missing -> adapter raises\n)\nadapter = LangChainToolAdapter(tool)\n\n# after\nfrom langchain_core.tools import tool as lc_tool\n\n@lc_tool\ndef fetch(q: str) -> str:\n    \"\"\"fetch data\"\"\"\n    return do_fetch(q)\n\nadapter = LangChainToolAdapter(fetch)","handlingStrategy":"type-guard","validationCode":"fn = getattr(tool, \"func\", None)\nrun = getattr(tool, \"_run\", None)\nif not (callable(fn) or callable(run)):\n    raise TypeError(f\"tool {tool!r} exposes no callable func/_run; wrap it with @tool first\")","typeGuard":"from typing import Any, Callable, TypeGuard\n\ndef is_adaptable_langchain_tool(tool: Any) -> TypeGuard[Any]:\n    fn = getattr(tool, \"func\", None)\n    run = getattr(tool, \"_run\", None)\n    return callable(fn) or callable(run)","tryCatchPattern":"try:\n    adapter = LangChainToolAdapter(tool)\nexcept AttributeError as e:\n    raise TypeError(f\"Cannot adapt {type(tool).__name__}: {e}\") from e","preventionTips":["Always create tools with the @tool decorator or StructuredTool.from_function instead of hand-constructing StructuredTool.","Check isinstance(tool, BaseTool) before adapting.","Pin langchain-core to a tested major version in your lockfile."],"tags":["langchain","interop","adapter","reflection"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}