# Playwright custom tool - should I use sync or async version?

**URL:** <https://community.crewai.com/t/playwright-custom-tool-should-i-use-sync-or-async-version/2163>\
**Category:** CrewAI Community Support\
**Tags:** tools\_issues\
**Created:** [December 20, 2024, 1:49pm UTC](https://community.crewai.com/t/playwright-custom-tool-should-i-use-sync-or-async-version/2163 "2024-12-20T13:49:51Z")\
**Posts on this page:** 3\
**Page:** 1

<div class="post-metadata">

**Author:** ![Alex\_Koniushenko](https://sea1.discourse-cdn.com/flex025/user_avatar/community.crewai.com/alex_koniushenko/32/1285_2.png) [@Alex\_Koniushenko](https://community.crewai.com/u/Alex_Koniushenko)\
**Post date:** [December 20, 2024, 1:49pm UTC](https://community.crewai.com/t/playwright-custom-tool-should-i-use-sync-or-async-version/2163/1 "2024-12-20T13:49:51Z")

</div>

I’ve created and tested separately my custom playwright\_tool - it works.  
But when I tried to use it in my crewai flow for 1 of agents - I got 2 types of warnings in logs:

- if I use sync version of playwright - it logs out:  
I encountered an error while trying to use the tool. This was the error: It looks like you are using Playwright Sync API inside the asyncio loop.  
Please use the Async API instead..
- if I use async version - it logs out coroutine, as the output of my tool:

# Agent: URL My Scraper

## Using tool: Playwright Scrape Website Tool

## Tool Input:

“{"url": "some\_url"}”

## Tool Output:

\<coroutine object PlaywrightTool.\_run at 0x000001C43A805F20\>

Sure, my \_run method is async:  
async def \_run(self, url: str) → dict:  
async with async\_playwright() as p:  
browser = await p.chromium.launch(headless=True)  
page = await browser.new\_page()  
await page.goto(url, wait\_until=“networkidle”)  
etc  
…  
…

But \_run is not awaited when crew is running.  
So how to handle this?

---

<div class="post-metadata">

**Author:** ![Ali\_Sogukpinar](https://sea1.discourse-cdn.com/flex025/user_avatar/community.crewai.com/ali_sogukpinar/32/3008_2.png) [@Ali\_Sogukpinar](https://community.crewai.com/u/Ali_Sogukpinar)\
**Post date:** [March 11, 2025, 7:06pm UTC](https://community.crewai.com/t/playwright-custom-tool-should-i-use-sync-or-async-version/2163/2 "2025-03-11T19:06:47Z")

</div>

Hi Alex,

did you make progress with Playwright tool. I was checking if there is already a tool available. It would be great if you share your experience or progress here.

Regards  
Ali

---

<div class="post-metadata">

**Author:** ![jojasan](https://sea1.discourse-cdn.com/flex025/user_avatar/community.crewai.com/jojasan/32/2675_2.png) [@jojasan](https://community.crewai.com/u/jojasan)\
**Post date:** [March 20, 2025, 4:20pm UTC](https://community.crewai.com/t/playwright-custom-tool-should-i-use-sync-or-async-version/2163/3 "2025-03-20T16:20:56Z")

</div>

tl;dr: try using the async implementation, use nest\_asyncio library, and attach the crewAI async loop

I’m having a very similar issue since I tried to implement a custom tool using Crawl4AI (which happens to have an async implementation). I think the problem seems to that CrewAI starts kickoff flows using async. See this code section:

```
<in crewai/flow/flow.py>
 def kickoff(self, inputs: Optional[Dict[str, Any]] = None) -> Any:
        """
        Start the flow execution in a synchronous context.

        This method wraps kickoff_async so that all state initialization and event
        emission is handled in the asynchronous method.
        """

        async def run_flow():
            return await self.kickoff_async(inputs)

        return asyncio.run(run_flow())

```

CrewAI seems to only work with non-async tools (that’s why you get the coroutine output).  
I found a way to make async tools work with CrewAI, but it requires a 3rd party library called nest\_asyncio (see: [nest-asyncio · PyPI](https://pypi.org/project/nest-asyncio/))

Here’s a code snippet that worked for me inside the tool:

```
# Keep _run synchronous as required
    def _run(self, **kwargs: Any) -> str:
        website_url = kwargs.get("website_url", self.website_url)

        try:
            loop = asyncio.get_running_loop()
            nest_asyncio.apply(loop) # this patches the loop created by CrewAI to allow nested asyncio.run
        except RuntimeError: # No running loop
            print("No running loop")
            return asyncio.run(self._crawl_content(website_url))
        else: # If loop is already running. This is required since crewAI kickoff executes a asyncio.run(run_flow())
            print("Attaching to existing async loop")
            return loop.run_until_complete(self._crawl_content(website_url))

    # Async helper method
    async def _crawl_content(self, website_url: str) -> str:
        # your code goes here

```
