DS DevShelfHub Projects · AI tools
Tutorials / CrewAI / Reference / Methods / BaseTool._arun()
Method Custom Tools

BaseTool._arun(): Reference Guide

By DevShelfHub

Async body of a tool — implemented when the tool wraps async I/O or external services.

See the CrewAI methods catalog, CrewAI introduction, kickoff() reference, and core concepts for surrounding context.

What is BaseTool._arun()?

_arun(**kwargs) is the awaitable counterpart of _run. CrewAI awaits it when the crew executes under kickoff_async so httpx.AsyncClient, aiohttp sessions, or async database drivers can share the same event-loop budget as the orchestrator. Arguments still flow through args_schema validation exactly like _run; only the execution model changes.

Implement both _run and _arun only when you must support synchronous kickoff() and async kickoff_async() with the same tool class — keep behavior aligned so switching execution modes does not change semantics. If your dependency stack is entirely async, you can lean on _arun alone when you control callers, but many codebases still need _run for scripts and notebooks.

Watch thread-pool misuse: dropping time.sleep or blocking boto3 calls inside _arun still stalls the loop. Offload truly blocking work to asyncio.to_thread or rewrite with native async clients.

Use Cases

  • Async APIs
  • Async DB queries

Key Features

  • Awaitable
  • Concurrent with other async work

When NOT to Use

Sync-only operations — _run() is enough.

Notes

kickoff() vs kickoff_async()

If agents still call kickoff(), CrewAI may never await _arun. Keep blocking IO in _run for sync paths, or standardize on kickoff_async end-to-end.

Client lifecycle

Creating a new AsyncClient per invocation adds TLS handshakes. Prefer injecting a shared client via __init__ when tools fire frequently.

Cancellation

When asyncio cancels the crew task, pending awaits inside _arun should exit cleanly — use async context managers and avoid swallowing CancelledError.

Return strings for the LLM

Mirroring _run, return text the model can consume. Serialize structured results with json.dumps instead of returning raw ORM rows.

Parameters

Parameter Type Required Purpose
**kwargs Any No Fields from args_schema.

Code Examples

Async fetch

python
async def _arun(self, url: str) -> str:
    async with httpx.AsyncClient() as c:
        return (await c.get(url)).text

Bounded parallel fetches inside one tool

python
import asyncio
import httpx
from crewai.tools import BaseTool

class MultiFetch(BaseTool):
    name = 'multi_fetch'
    description = 'GET several URLs concurrently'

    async def _arun(self, urls: list[str]) -> str:
        async with httpx.AsyncClient(timeout=20) as c:
            tasks = [c.get(u) for u in urls[:5]]
            resps = await asyncio.gather(*tasks, return_exceptions=True)
        return '\n'.join(str(r) for r in resps)

Delegate blocking SDK to a thread

python
import asyncio
from crewai.tools import BaseTool

class LegacySdkTool(BaseTool):
    async def _arun(self, query: str) -> str:
        return await asyncio.to_thread(self._sdk.search, query)

When to Use

Tools wrapping httpx/aiohttp clients or async DB drivers.

Common Mistakes

❌ Calling sync requests inside _arun

✅ Use httpx.AsyncClient or run sync code in a thread.

Related: @tool decorator reference, Agent class reference, and the first Crew tutorial.

BaseTool._arun() FAQ

What is BaseTool._arun() in CrewAI?

Async body of a tool — implemented when the tool wraps async I/O or external services. _arun(**kwargs) is the awaitable counterpart of _run. CrewAI awaits it when the crew executes under kickoff_async so httpx.AsyncClient, aiohttp sessions, or async database drivers can share the same event-loop budget as the orchestrator. Arguments still flow through args_schema validation exactly like _run; only the execution model changes. Implement both _run and _arun only when you must support synchronous kickoff() and async kickoff_async() with the same tool class — keep…

Which CrewAI types expose the method BaseTool._arun()?

DevShelfHub documents BaseTool._arun() on Custom Tools. The reference maps it to Python module crewai.tools.BaseTool — pin your installed crewai version and match imports to the snippet on this page.

When should I use BaseTool._arun()?

Tools wrapping httpx/aiohttp clients or async DB drivers.

When should I avoid BaseTool._arun()?

Sync-only operations — _run() is enough.

How do I call BaseTool._arun() from Python?

async def _arun(self, **kw): return await do(**kw)

Where can I explore more CrewAI API reference pages?

Open the CrewAI API reference index on DevShelfHub to search classes, methods, and decorators, each with runnable examples, parameters, common mistakes, and cross-links.