langchain - 💡(How to fix) Fix bug(core): StructuredTool._run() fails when only coroutine is available

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

StructuredTool._run() only checks self.func. When func is None but coroutine is available (like MCP tools from langchain-mcp-adapters), it raises NotImplementedError instead of falling back to async execution.

The contrast with ainvoke() is notable: ainvoke() correctly falls back to run_in_executor() when coroutine is None. But _run() does not have the symmetric fallback.

This prevents MCP tools (which are async-only) from working with frameworks that use sync invoke() (e.g., LangGraph's ToolNode._execute_tool_sync).

Error Message

import asyncio from langchain_core.tools import StructuredTool from pydantic import BaseModel

class MyInput(BaseModel): path: str

async def my_coro(path: str): return f"content of {path}"

tool = StructuredTool( name="read_file", description="Read a file", args_schema=MyInput, coroutine=my_coro, )

This works

async def main(): result = await tool.ainvoke({"path": "/tmp/test.txt"}) print(f"async: {result}")

asyncio.run(main())

This fails

result = tool.invoke({"path": "/tmp/test.txt"})

NotImplementedError: StructuredTool does not support sync invocation.

Root Cause

StructuredTool._run() only checks self.func. When func is None but coroutine is available (like MCP tools from langchain-mcp-adapters), it raises NotImplementedError instead of falling back to async execution.

The contrast with ainvoke() is notable: ainvoke() correctly falls back to run_in_executor() when coroutine is None. But _run() does not have the symmetric fallback.

This prevents MCP tools (which are async-only) from working with frameworks that use sync invoke() (e.g., LangGraph's ToolNode._execute_tool_sync).

Code Example

import asyncio
from langchain_core.tools import StructuredTool
from pydantic import BaseModel

class MyInput(BaseModel):
    path: str

async def my_coro(path: str):
    return f"content of {path}"

tool = StructuredTool(
    name="read_file",
    description="Read a file",
    args_schema=MyInput,
    coroutine=my_coro,
)

# This works
async def main():
    result = await tool.ainvoke({"path": "/tmp/test.txt"})
    print(f"async: {result}")

asyncio.run(main())

# This fails
result = tool.invoke({"path": "/tmp/test.txt"})
# NotImplementedError: StructuredTool does not support sync invocation.

---

NotImplementedError: StructuredTool does not support sync invocation.
RAW_BUFFERClick to expand / collapse

Checked other resources

  • I added a very descriptive title to this issue.
  • I used the GitHub search to find a similar issue and didn't find any.
  • I searched the LangChain documentation for a solution.

Example Code

import asyncio
from langchain_core.tools import StructuredTool
from pydantic import BaseModel

class MyInput(BaseModel):
    path: str

async def my_coro(path: str):
    return f"content of {path}"

tool = StructuredTool(
    name="read_file",
    description="Read a file",
    args_schema=MyInput,
    coroutine=my_coro,
)

# This works
async def main():
    result = await tool.ainvoke({"path": "/tmp/test.txt"})
    print(f"async: {result}")

asyncio.run(main())

# This fails
result = tool.invoke({"path": "/tmp/test.txt"})
# NotImplementedError: StructuredTool does not support sync invocation.

Error Message and Stack Trace (if applicable)

NotImplementedError: StructuredTool does not support sync invocation.

Description

StructuredTool._run() only checks self.func. When func is None but coroutine is available (like MCP tools from langchain-mcp-adapters), it raises NotImplementedError instead of falling back to async execution.

The contrast with ainvoke() is notable: ainvoke() correctly falls back to run_in_executor() when coroutine is None. But _run() does not have the symmetric fallback.

This prevents MCP tools (which are async-only) from working with frameworks that use sync invoke() (e.g., LangGraph's ToolNode._execute_tool_sync).

System Info

  • langchain-core: 0.3.x
  • Python: 3.11+
  • OS: macOS

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING

langchain - 💡(How to fix) Fix bug(core): StructuredTool._run() fails when only coroutine is available