Summary
When calling client.call_tool(name, task=True, raise_on_error=True) and the server doesn't support tasks (graceful degradation), raise_on_error is silently dropped. Errors are swallowed instead of raised.
Reproduction
from fastmcp import FastMCP, Client
server = FastMCP('test')
@server.tool
def failing_tool() -> str:
"""Always fails"""
raise ValueError('intentional error')
async with Client(server) as client:
# Non-task path: correctly raises ToolError
await client.call_tool('failing_tool', raise_on_error=True) # raises ✅
# Task path with graceful degradation: should raise but doesn't
task = await client.call_tool('failing_tool', task=True, raise_on_error=True)
result = await task.result()
print(result.is_error) # True — error was silently swallowed ❌
Root Cause
call_tool() (tools.py:276) calls _call_tool_as_task() without forwarding raise_on_error:
if task:
return await self._call_tool_as_task(
name, arguments, task_id, ttl, meta=request_meta or None
)
# ← raise_on_error not passed
_call_tool_as_task() (tools.py:291) doesn't accept raise_on_error at all. In the graceful degradation path (line 351), it calls _parse_call_tool_result without it, which defaults to raise_on_error=False:
parsed_result = await self._parse_call_tool_result(name, raw_result)
# ← defaults to raise_on_error=False
Fix
- Add
raise_on_error: bool = True parameter to _call_tool_as_task()
- Pass it through from
call_tool() at line 276
- Forward it to
_parse_call_tool_result() at line 351
Summary
When calling
client.call_tool(name, task=True, raise_on_error=True)and the server doesn't support tasks (graceful degradation),raise_on_erroris silently dropped. Errors are swallowed instead of raised.Reproduction
Root Cause
call_tool()(tools.py:276) calls_call_tool_as_task()without forwardingraise_on_error:_call_tool_as_task()(tools.py:291) doesn't acceptraise_on_errorat all. In the graceful degradation path (line 351), it calls_parse_call_tool_resultwithout it, which defaults toraise_on_error=False:Fix
raise_on_error: bool = Trueparameter to_call_tool_as_task()call_tool()at line 276_parse_call_tool_result()at line 351