Skip to content

fix(tools): return structured JSON error data from tool exceptions#6313

Open
AHMEDDEV2004 wants to merge 1 commit into
crewAIInc:mainfrom
AHMEDDEV2004:fix/structured-tool-errors-6262
Open

fix(tools): return structured JSON error data from tool exceptions#6313
AHMEDDEV2004 wants to merge 1 commit into
crewAIInc:mainfrom
AHMEDDEV2004:fix/structured-tool-errors-6262

Conversation

@AHMEDDEV2004

@AHMEDDEV2004 AHMEDDEV2004 commented Jun 24, 2026

Copy link
Copy Markdown

Summary

Closes #6262

When a tool raises an exception, CrewAI currently catches it and returns a generic string like "Error executing tool: {e}" — discarding the exception type, retryability classification, and structured data that agents need to make informed decisions.

This PR replaces all generic error strings with structured JSON containing:

  • error: true — unambiguous error flag
  • type — exception class name (e.g. "TimeoutError", "ValueError")
  • message — original exception message
  • retryable — boolean hint based on exception type (True for TimeoutError, ConnectionError, OSError)

Before

Error executing tool: connection timed out

After

Error executing tool: {"error": true, "type": "TimeoutError", "message": "connection timed out", "retryable": true}

The "Error executing tool:" prefix is preserved for backward compatibility with existing parsing logic.

Changes

  • New: crewai/utilities/tool_errors.pyformat_tool_error() utility with retryable classification
  • Modified: 4 files where generic error strings were returned:
    • agents/crew_agent_executor.py (native tool call handler)
    • experimental/agent_executor.py (3 locations: ReAct loop, parallel tool calls, native tool handler)
    • utilities/agent_utils.py (shared tool execution utility)
    • llm.py (event emissions for LLMCallFailedEvent and ToolUsageErrorEvent)
  • New: tests/test_tool_errors.py — 17 unit tests covering all error scenarios

Design Decisions

  1. Structured JSON over raw traceback — Agents can parse JSON to make decisions; raw tracebacks waste context tokens and aren't actionable by an LLM.
  2. retryable hint — Lets agents automatically retry transient failures (network, timeout) without wasting iterations on permanent errors (validation, type errors).
  3. Backward-compatible prefix — Keeps "Error executing tool:" so any existing regex/startswith checks continue to work.
  4. No traceback by default — Available via include_traceback=True for debugging, but excluded by default to save LLM context window.

Test plan

  • All 17 unit tests pass for format_tool_error()
  • Verified backward compatibility (prefix preserved)
  • Syntax validation passes for all 6 modified/new files
  • Existing test suite (pytest lib/crewai/tests/agents/) — CI will verify

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Tool and function errors are now returned in a consistent, structured format.
    • Error details now include the exception type, message, and whether the error may be retryable.
  • Bug Fixes

    • Improved how tool execution failures appear in agent history and event outputs.
    • Added clearer handling for tracebacks when available, while keeping them hidden by default.

Replace generic "Error executing tool: {e}" strings with structured
JSON containing exception type, message, and retryability hint.
This gives agents the information needed to decide whether to retry,
fix their input, or skip the tool entirely.

Closes crewAIInc#6262

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>

@corridor-security corridor-security 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.

Summary: This PR changes tool exception handling to return structured JSON error metadata while preserving the existing error prefix.

Risk: Low risk. No exploitable security vulnerabilities were identified; the changes do not add authentication, authorization, data access, network, subprocess, or file-handling attack surfaces.

@coderabbitai

coderabbitai Bot commented Jun 24, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

A new tool_errors.py utility module is added defining RETRYABLE_EXCEPTIONS and format_tool_error, which returns a structured JSON string prefixed with "Error executing tool:" containing the exception type, message, retryability flag, and optional traceback. All four tool-execution exception handlers in crew_agent_executor, experimental/agent_executor, llm, and agent_utils are updated to use this formatter instead of ad-hoc error strings.

Changes

Structured Tool Error Formatting

Layer / File(s) Summary
tool_errors module and tests
lib/crewai/src/crewai/utilities/tool_errors.py, lib/crewai/tests/test_tool_errors.py
Defines RETRYABLE_EXCEPTIONS = (TimeoutError, ConnectionError, OSError) and format_tool_error(exception, include_traceback=False) returning an "Error executing tool:" prefix followed by JSON with error, type, message, retryable, and optional traceback fields. Tests cover output format, retryability per exception type, traceback inclusion, edge-case messages, and custom subclasses.
Call-site adoption
lib/crewai/src/crewai/agents/crew_agent_executor.py, lib/crewai/src/crewai/experimental/agent_executor.py, lib/crewai/src/crewai/llm.py, lib/crewai/src/crewai/utilities/agent_utils.py
Imports format_tool_error and replaces hardcoded f"Error executing tool: {e}" and f"Tool execution error: {e!s}" strings at every exception handler: _execute_single_native_tool_call in both executors, the parallel native tool path in experimental executor, execute_tool_action legacy path, and _handle_tool_call in llm.py (including both LLMCallFailedEvent and ToolUsageErrorEvent error fields).
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 25.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: structured JSON error data for tool exceptions.
Linked Issues check ✅ Passed The changes address #6262 by preserving exception type, message, retryability, and optional traceback in structured tool errors.
Out of Scope Changes check ✅ Passed The PR stays focused on tool error formatting and related tests, with no clear unrelated code changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ 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: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
lib/crewai/src/crewai/utilities/agent_utils.py (1)

1549-1565: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Align ToolUsageErrorEvent.error with the new structured error contract

This path formats result with format_tool_error(e) but still emits ToolUsageErrorEvent(error=e), producing mixed error payload shapes across call paths.

Suggested fix
             except Exception as e:
-                result = format_tool_error(e)
+                structured_error = format_tool_error(e)
+                result = structured_error
                 raw_tool_result = result
                 if task:
                     task.increment_tools_errors()
                 crewai_event_bus.emit(
                     event_source,
                     event=ToolUsageErrorEvent(
                         tool_name=func_name,
                         tool_args=args_dict,
                         from_agent=agent,
                         from_task=task,
                         agent_key=agent_key,
                         plan_step_number=plan_step_number,
                         plan_step_description=plan_step_description,
-                        error=e,
+                        error=structured_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/utilities/agent_utils.py` around lines 1549 - 1565, The
ToolUsageErrorEvent payload is still emitting the raw exception while this path
already converts it to the structured form via format_tool_error in agent_utils;
update the ToolUsageErrorEvent construction in the exception handler to use the
same structured error value as raw_tool_result/result so the error contract is
consistent across call paths. Locate the emit call in the except block around
ToolUsageErrorEvent and align its error field with the formatted error object
rather than passing e directly.
🤖 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/src/crewai/utilities/tool_errors.py`:
- Around line 27-28: The traceback handling in tool error formatting should use
the passed exception’s own traceback instead of relying on ambient exception
state. Update the logic in the utility that builds error details (the block
guarded by include_traceback in the tool error formatter) to format from the
exception object’s __traceback__ rather than calling traceback.format_exc(), so
the recorded traceback is accurate even when not inside an active except block.

---

Outside diff comments:
In `@lib/crewai/src/crewai/utilities/agent_utils.py`:
- Around line 1549-1565: The ToolUsageErrorEvent payload is still emitting the
raw exception while this path already converts it to the structured form via
format_tool_error in agent_utils; update the ToolUsageErrorEvent construction in
the exception handler to use the same structured error value as
raw_tool_result/result so the error contract is consistent across call paths.
Locate the emit call in the except block around ToolUsageErrorEvent and align
its error field with the formatted error object rather than passing e directly.
🪄 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: 6670a8b3-1c28-4413-ba15-ac5ea44f0e92

📥 Commits

Reviewing files that changed from the base of the PR and between a046e6a and dee2e38.

📒 Files selected for processing (6)
  • lib/crewai/src/crewai/agents/crew_agent_executor.py
  • lib/crewai/src/crewai/experimental/agent_executor.py
  • lib/crewai/src/crewai/llm.py
  • lib/crewai/src/crewai/utilities/agent_utils.py
  • lib/crewai/src/crewai/utilities/tool_errors.py
  • lib/crewai/tests/test_tool_errors.py

Comment on lines +27 to +28
if include_traceback:
error_data["traceback"] = traceback.format_exc(limit=3)

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

Use the passed exception traceback instead of ambient exception state.

On Line 28, traceback.format_exc() can emit NoneType: None if this formatter is called outside an active except block. Format from exception.__traceback__ directly to keep traceback accurate.

Proposed fix
-    if include_traceback:
-        error_data["traceback"] = traceback.format_exc(limit=3)
+    if include_traceback:
+        error_data["traceback"] = "".join(
+            traceback.format_exception(
+                type(exception), exception, exception.__traceback__, limit=3
+            )
+        )
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if include_traceback:
error_data["traceback"] = traceback.format_exc(limit=3)
if include_traceback:
error_data["traceback"] = "".join(
traceback.format_exception(
type(exception), exception, exception.__traceback__, limit=3
)
)
🧰 Tools
🪛 ast-grep (0.44.0)

[info] 28-28: use jsonify instead of json.dumps for JSON output
Context: json.dumps(error_data)
Note: [CWE-116] Improper Encoding or Escaping of Output.

(use-jsonify)

🤖 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/utilities/tool_errors.py` around lines 27 - 28, The
traceback handling in tool error formatting should use the passed exception’s
own traceback instead of relying on ambient exception state. Update the logic in
the utility that builds error details (the block guarded by include_traceback in
the tool error formatter) to format from the exception object’s __traceback__
rather than calling traceback.format_exc(), so the recorded traceback is
accurate even when not inside an active except block.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

bug: tool exceptions are caught and replaced with generic error messages — lose root cause

2 participants