Skip to content

fix: skip result_as_answer early-return when tool execution raised an error#6555

Open
AdeevMardia2008 wants to merge 1 commit into
crewAIInc:mainfrom
AdeevMardia2008:fix/result-as-answer-ignores-tool-errors
Open

fix: skip result_as_answer early-return when tool execution raised an error#6555
AdeevMardia2008 wants to merge 1 commit into
crewAIInc:mainfrom
AdeevMardia2008:fix/result-as-answer-ignores-tool-errors

Conversation

@AdeevMardia2008

Copy link
Copy Markdown

Summary

Fixes #5156

When a tool raises an exception during execution, the result string is set to an error message (e.g. "Error executing tool: ...") and the agent is supposed to reflect on the failure and retry. However, if the tool had result_as_answer=True, _append_tool_result_and_check_finality would unconditionally return that error string as an AgentFinish — the final answer — bypassing the agent's retry loop entirely.

Root Cause

In _append_tool_result_and_check_finality (crew_agent_executor.py), the result_as_answer check had no guard for error cases:

if (
    original_tool
    and hasattr(original_tool, "result_as_answer")
    and original_tool.result_as_answer
):
    return AgentFinish(
        thought="Tool result is the final answer",
        output=result,   # <-- could be "Error executing tool: ..."
        text=result,
    )

Fix

Three minimal changes:

  1. _execute_single_native_tool_call — add "is_error": error_event_emitted to the returned dict. error_event_emitted is already set to True whenever the tool raises an exception.

  2. _append_tool_result_and_check_finality — extract is_error from the dict (defaulting to False for forward compatibility).

  3. Guard the result_as_answer early-return with and not is_error:

if (
    original_tool
    and hasattr(original_tool, "result_as_answer")
    and original_tool.result_as_answer
    and not is_error          # NEW: don't short-circuit on error output
):
    return AgentFinish(...)

When the tool raises, the method returns None instead of AgentFinish, so the error is appended to the conversation as a tool message and the agent can reflect and retry normally.

Behaviour After Fix

Scenario Before After
Tool succeeds, result_as_answer=True ✅ Returns result as final answer ✅ Same
Tool raises, result_as_answer=True ❌ Error string becomes final answer ✅ Agent can reflect & retry
Tool raises, result_as_answer=False ✅ Agent reflects & retries ✅ Same

Testing

Reproducer (no API key needed — uses a StubLLM):

from crewai import Agent, Crew, Task
from crewai.tools import BaseTool

class FailingTool(BaseTool):
    name: str = "failing_tool"
    description: str = "Always fails"
    result_as_answer: bool = True

    def _run(self, **kwargs):
        raise ValueError("simulated tool error")

# Before fix: crew.kickoff() returns "Error executing tool: simulated tool error"
# After fix:  agent receives the error and can retry / reflect

When a tool raises an exception, the result string is an error message
(e.g. "Error executing tool: ..."). Previously, if the tool had
result_as_answer=True, this error string was returned as AgentFinish —
the final answer — without giving the agent a chance to reflect and retry.

Fix: propagate is_error from _execute_single_native_tool_call through
the execution_result dict, and guard the result_as_answer early-return
in _append_tool_result_and_check_finality with `and not is_error`.

Fixes crewAIInc#5156
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 06efc0f1-03c2-42e1-9b2a-a50428a3a279

📥 Commits

Reviewing files that changed from the base of the PR and between da9902d and 72cdba4.

📒 Files selected for processing (1)
  • lib/crewai/src/crewai/agents/crew_agent_executor.py

📝 Walkthrough

Walkthrough

Native tool execution now uses centralized hook runners, returns whether execution emitted an error, and prevents errored tools configured with result_as_answer from terminating the agent with the error output.

Changes

Native tool execution

Layer / File(s) Summary
Centralized native tool hook execution
lib/crewai/src/crewai/agents/crew_agent_executor.py
Native tool calls use run_before_tool_call_hooks for blocking decisions and run_after_tool_call_hooks for optional result replacement.
Error-aware tool finality
lib/crewai/src/crewai/agents/crew_agent_executor.py
Native tool results include an is_error flag, and result_as_answer only produces a final answer when the tool did not error.

Sequence Diagram(s)

sequenceDiagram
  participant CrewAgentExecutor
  participant run_before_tool_call_hooks
  participant NativeTool
  participant run_after_tool_call_hooks
  CrewAgentExecutor->>run_before_tool_call_hooks: Execute before-tool hooks
  run_before_tool_call_hooks-->>CrewAgentExecutor: Return blocked state
  CrewAgentExecutor->>NativeTool: Execute native tool
  NativeTool-->>CrewAgentExecutor: Return result or emit error
  CrewAgentExecutor->>run_after_tool_call_hooks: Process tool result
  run_after_tool_call_hooks-->>CrewAgentExecutor: Return optional replacement result
  CrewAgentExecutor->>CrewAgentExecutor: Apply error-aware finality
Loading

Suggested reviewers: lucasgomide

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Linked Issues check ⚠️ Warning The fix matches the bug report, but the linked issue also calls for tests covering success and failure cases, which are not shown here. Add or update tests for both successful and failed tool calls with result_as_answer=True, including failure-retry behavior.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly and specifically describes the main behavior change in the PR.
Description check ✅ Passed The description is detailed and directly matches the implemented fix.
Out of Scope Changes check ✅ Passed The change stays focused on tool error handling and final-answer logic with no obvious unrelated edits.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
✨ 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.

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

Labels

None yet

Projects

None yet

1 participant