datawhalechina/hello-agents · error · TimeoutException

工具 '{tool_name}' 执行超时

Error message

工具 '{tool_name}' 执行超时

What it means

TimeoutException from BaseAgent.call_tool: asyncio.wait_for wrapped the tool coroutine (or asyncio.to_thread for sync tools) with self.timeout seconds, the deadline elapsed, and wait_for cancelled the task. The message names the tool that hung. Important caveat: for to_thread tools the underlying thread cannot be force-killed, so it may keep running after the exception.

Source

Thrown at Co-creation-projects/Apricity-InnocoreAI/agents/base.py:82

            tool_func = self.tools[tool_name]["function"]
            if asyncio.iscoroutinefunction(tool_func):
                result = await asyncio.wait_for(
                    tool_func(tool_input), 
                    timeout=self.timeout
                )
            else:
                result = await asyncio.wait_for(
                    asyncio.to_thread(tool_func, tool_input),
                    timeout=self.timeout
                )
            
            self._add_to_history(f"Tool {tool_name} called with input: {tool_input}")
            self._add_to_history(f"Tool {tool_name} result: {result}")
            
            return result
            
        except asyncio.TimeoutError:
            raise TimeoutException(f"工具 '{tool_name}' 执行超时")
        except Exception as e:
            raise AgentException(f"工具 '{tool_name}' 执行失败: {str(e)}")
    
    async def think(self, prompt: str, context: Dict = None) -> str:
        """调用LLM进行思考"""
        try:
            # 构建完整的提示词
            full_prompt = prompt
            
            # 添加上下文信息
            if context:
                context_str = json.dumps(context, ensure_ascii=False, indent=2)
                full_prompt = f"上下文信息:\n{context_str}\n\n任务:\n{prompt}"
            
            # 添加历史记录
            if self.history:
                history_str = "\n".join(self.history[-10:])  # 只保留最近10条
                full_prompt += f"\n\n历史记录:\n{history_str}"

View on GitHub (pinned to 606a07d341)

Solutions

  1. Raise self.timeout for network tools or make it per-tool configurable (e.g. timeout=120 for download-heavy tools).
  2. Set aiohttp.ClientTimeout(total=..., sock_read=30) inside the tool so it fails fast with its own clear error before the agent deadline.
  3. Replace blocking requests with async clients so wait_for's cancellation actually stops the work.
  4. Retry transient network hangs with a bounded retry loop inside the tool (backoff on the API call, not the whole agent step).
  5. For CPU-bound sync tools, move heavy parsing into a subprocess if cancellation must actually stop it.

Example fix

# before
async with session.get(self.arxiv_base_url, params=params) as response:
    ...  # no inner timeout; agent-level wait_for fires

# after — tool owns its timeout so it fails (and retries) before the agent deadline
timeout = aiohttp.ClientTimeout(total=30, sock_read=10)
async with aiohttp.ClientSession(timeout=timeout) as session:
    for attempt in range(3):
        try:
            async with session.get(self.arxiv_base_url, params=params) as response:
                ...
            break
        except aiohttp.ClientError:
            if attempt == 2:
                raise
Defensive patterns

Strategy: retry

Validate before calling

# Verify tool timeouts are sane before running the agent
assert agent.timeout >= 30, f"timeout={agent.timeout}s too low for network tools"
for name, info in agent.tools.items():
    if asyncio.iscoroutinefunction(info['function']):
        continue  # async tools can be cancelled cleanly
    log.warning(f"sync tool '{name}' blocks the loop thread if it hangs")

Try / catch

from agents.exceptions import TimeoutException
try:
    result = await agent.call_tool('search_arxiv', q)
except TimeoutException as e:
    result = await with_backoff(lambda: agent.call_tool('search_arxiv', q), retries=2)

Prevention

When it happens

Trigger: Calling a network-bound tool (ArXiv/IEEE search, PDF download) against a slow or stalled endpoint with the default agent timeout; a sync tool doing blocking I/O inside to_thread while the event loop waits; tool deadlocking on a lock or unbounded feedparser parse of a huge response.

Common situations: Default timeout too low for arXiv's occasionally slow API; aiohttp session without its own sock_read timeout so the coroutine never progresses; tools doing requests.get (blocking) instead of aiohttp; thread-pool saturation making even fast tools exceed the budget.

Related errors


AI-assisted analysis of datawhalechina/hello-agents@606a07d341 (2026-08-14). Data as JSON: /api/errors/ac956ea94b544472. Report an issue: GitHub.