Skip to content

fix: await native async tools on caller event loop (#6611)#6620

Open
Diwak4r wants to merge 3 commits into
crewAIInc:mainfrom
Diwak4r:fix/native-async-tools-await-loop-6611
Open

fix: await native async tools on caller event loop (#6611)#6620
Diwak4r wants to merge 3 commits into
crewAIInc:mainfrom
Diwak4r:fix/native-async-tools-await-loop-6611

Conversation

@Diwak4r

@Diwak4r Diwak4r commented Jul 23, 2026

Copy link
Copy Markdown

Summary

Fixes #6611: Async tools in the experimental native function calling executor were not being awaited natively on the caller's event loop. Instead, returned a bare coroutine that bridged via per call inside the worker thread — creating a fresh nested event loop per tool invocation.

Root Cause

In , tool functions were called synchronously:

When is an async coroutine function (as registered by ), this returns an unawaited coroutine object. then detects the coroutine and runs it via inside the worker thread — a fresh event loop per call.

Solution

  1. Capture the parent event loop in via a module-level () before entering the . propagates this to worker threads.

  2. Add helper that:

    • Calls the tool function
    • Detects if the result is a coroutine via
    • If async, schedules it on the parent loop via and blocks on the future
    • Falls back to if no parent loop is in scope (defensive)
  3. Replace the direct call at line 2008 with

Testing

Added two new tests in :

  • — verifies async tools run on the parent thread's event loop
  • — ensures sync tools still work without an event loop

All 6 native tool execution tests pass. The fix mirrors the ReAct executor's pattern where properly awaits coroutines.

Diwak4r and others added 2 commits July 22, 2026 19:25
…adError

Two bare `raise` statements in get_uploader() were outside any exception
handler, causing RuntimeError: No active exception to re-raise instead
of a meaningful exception. Replaced with PermanentUploadError and
descriptive messages.

- Line 199 (Bedrock S3 bucket not configured)
- Line 216 (unknown provider)

Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
- Capture parent event loop in ContextVar before dispatching to ThreadPoolExecutor
- Add _invoke_native_tool_func helper that detects coroutines and schedules them
  on the parent loop via asyncio.run_coroutine_threadsafe()
- Fall back to asyncio.run() when no parent loop exists (defensive)
- Reset ContextVar in finally block to avoid leakage
- Add regression tests for async tool execution on parent loop and sync tool fallback
Copilot AI review requested due to automatic review settings July 23, 2026 08:08

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

…crewAIInc#6430)

The bare `raise` at line 853 was outside any exception handler context,
causing `RuntimeError: No active exception to re-raise` when
`_validate_tool_input` returned a non-dict value and `raise_error=True`.

Fixed by raising `ToolUsageError` with the existing i18n message instead,
matching the `raise_error=False` branch behavior.
Copilot AI review requested due to automatic review settings July 23, 2026 08:40

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot was unable to review this pull request because the user who requested the review has reached their quota limit.

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Changes

Native tool event-loop propagation

Layer / File(s) Summary
Parent-loop capture and native invocation
lib/crewai/src/crewai/experimental/agent_executor.py
Native tool execution propagates the parent event loop into worker threads, schedules coroutine-returning tools on it, and resets the ContextVar afterward.
Native tool execution regression coverage
lib/crewai/tests/agents/test_agent_executor.py
Tests cover async dispatch, ContextVar cleanup, and synchronous native tools.

Uploader factory errors

Layer / File(s) Summary
Typed uploader failure paths
lib/crewai-files/src/crewai_files/uploaders/factory.py, lib/crewai-files/tests/test_uploader_factory.py
Missing Bedrock/S3 configuration and unsupported providers now raise PermanentUploadError with specific messages; provider behavior is tested.

Tool input error normalization

Layer / File(s) Summary
Localized validation error
lib/crewai/src/crewai/tools/tool_usage.py
The raise-error validation path now raises ToolUsageError with the localized tool argument error message.

Sequence Diagram(s)

sequenceDiagram
  participant AgentExecutor
  participant ThreadPoolExecutor
  participant NativeTool
  participant ParentEventLoop
  AgentExecutor->>ParentEventLoop: capture running loop
  AgentExecutor->>ThreadPoolExecutor: execute native tool call
  ThreadPoolExecutor->>NativeTool: invoke callable
  NativeTool-->>ThreadPoolExecutor: return coroutine
  ThreadPoolExecutor->>ParentEventLoop: schedule coroutine
  ParentEventLoop-->>ThreadPoolExecutor: return result
  AgentExecutor->>AgentExecutor: reset loop ContextVar
Loading

Suggested reviewers: copilot

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Out of Scope Changes check ⚠️ Warning The uploader factory and tool usage error-handling edits are unrelated to #6611 and appear out of scope. Move the uploader and tool_usage fixes into separate PRs unless they are part of the linked issue's scope.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed The agent executor changes implement caller-loop coroutine scheduling and tests cover the reported async-tool bug.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Title check ✅ Passed The title clearly matches the main change: awaiting native async tools on the caller event loop.
Description check ✅ Passed The description is directly about the same async tool execution fix and regression tests.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (1)
lib/crewai-files/src/crewai_files/uploaders/factory.py (1)

200-203: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Document the new permanent-error contract.

get_uploader now raises PermanentUploadError for unsupported providers and missing Bedrock configuration, while its docstring still promises None for unsupported providers. Update Returns and add Raises.

Proposed documentation update
     Returns:
-        FileUploader instance for the provider, or None if not supported.
+        FileUploader instance for the provider.
+
+    Raises:
+        PermanentUploadError: If the provider is unsupported or Bedrock lacks
+            required S3 bucket configuration.
+        ImportError: If the provider's optional SDK is not installed.

Also applies to: 220-222

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai-files/src/crewai_files/uploaders/factory.py` around lines 200 -
203, Update the get_uploader docstring to remove the outdated promise of
returning None for unsupported providers, document its actual return behavior,
and add a Raises section covering PermanentUploadError for unsupported providers
and missing Bedrock S3 configuration.

Source: Coding guidelines

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@lib/crewai-files/tests/test_uploader_factory.py`:
- Line 15: Isolate Bedrock configuration in both no-config tests by using the
suite’s environment-isolation fixture to clear CREWAI_BEDROCK_S3_BUCKET before
get_uploader("bedrock") at lib/crewai-files/tests/test_uploader_factory.py lines
15-15 and before the configuration-error assertion at lines 54-54.

In `@lib/crewai/src/crewai/tools/tool_usage.py`:
- Line 853: Move the localized ToolUsageError conversion into the exception
handler around _validate_tool_input(), replacing the bare raise used for actual
validation failures. Remove the unreachable not isinstance(arguments, dict)
branch while preserving successful dictionary results and the existing
tool_arguments_error message.

---

Nitpick comments:
In `@lib/crewai-files/src/crewai_files/uploaders/factory.py`:
- Around line 200-203: Update the get_uploader docstring to remove the outdated
promise of returning None for unsupported providers, document its actual return
behavior, and add a Raises section covering PermanentUploadError for unsupported
providers and missing Bedrock S3 configuration.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 98572d06-cdea-4f80-90cf-b0260c5c8b17

📥 Commits

Reviewing files that changed from the base of the PR and between b14d36b and 522d052.

📒 Files selected for processing (5)
  • lib/crewai-files/src/crewai_files/uploaders/factory.py
  • lib/crewai-files/tests/test_uploader_factory.py
  • lib/crewai/src/crewai/experimental/agent_executor.py
  • lib/crewai/src/crewai/tools/tool_usage.py
  • lib/crewai/tests/agents/test_agent_executor.py

not RuntimeError."""
raised = False
try:
get_uploader("bedrock")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Isolate the Bedrock configuration in both “no config” tests.

A CI environment with CREWAI_BEDROCK_S3_BUCKET set will take the configured Bedrock path, causing these tests to fail or exercise optional SDK behavior instead. Clear the variable with the suite’s environment-isolation fixture before each call.

  • lib/crewai-files/tests/test_uploader_factory.py#L15-L15: clear CREWAI_BEDROCK_S3_BUCKET before invoking get_uploader("bedrock").
  • lib/crewai-files/tests/test_uploader_factory.py#L54-L54: clear CREWAI_BEDROCK_S3_BUCKET before asserting the configuration-error message.
📍 Affects 1 file
  • lib/crewai-files/tests/test_uploader_factory.py#L15-L15 (this comment)
  • lib/crewai-files/tests/test_uploader_factory.py#L54-L54
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai-files/tests/test_uploader_factory.py` at line 15, Isolate Bedrock
configuration in both no-config tests by using the suite’s environment-isolation
fixture to clear CREWAI_BEDROCK_S3_BUCKET before get_uploader("bedrock") at
lib/crewai-files/tests/test_uploader_factory.py lines 15-15 and before the
configuration-error assertion at lines 54-54.

if not isinstance(arguments, dict):
if raise_error:
raise
raise ToolUsageError(f"{I18N_DEFAULT.errors('tool_arguments_error')}")

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Normalize the exception in the actual validation failure path.

_validate_tool_input() only returns dictionaries or raises, so this not isinstance(arguments, dict) branch is unreachable. Actual validation errors still hit the bare raise at Lines 846-848, meaning the localized ToolUsageError is not produced as intended. Move the conversion into that except block.

Proposed fix
-        except Exception:
+        except Exception as e:
             if raise_error:
-                raise
+                raise ToolUsageError(
+                    I18N_DEFAULT.errors("tool_arguments_error")
+                ) from e
             return ToolUsageError(f"{I18N_DEFAULT.errors('tool_arguments_error')}")
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@lib/crewai/src/crewai/tools/tool_usage.py` at line 853, Move the localized
ToolUsageError conversion into the exception handler around
_validate_tool_input(), replacing the bare raise used for actual validation
failures. Remove the unreachable not isinstance(arguments, dict) branch while
preserving successful dictionary results and the existing tool_arguments_error
message.

@chatgpt-codex-connector

Copy link
Copy Markdown

You have reached your Codex usage limits for code reviews. You can see your limits in the Codex usage dashboard.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

2 participants