#5802·crewAI

Tool re-execution on task retry has no idempotency guard — duplicate payments, emails, trades possible

Author: azender1Created May 14, 2026Updated Sep 17, 2026
Labelsbug

Description

When a CrewAI task fails and is retried — via max_retry_limit, exception handling, or external re-trigger — any @tool decorated function that already executed runs again. There's no mechanism to detect that a specific tool call already completed.

Steps to Reproduce

  1. Create a CrewAI agent with a @tool that calls an external API (payment, email, etc.)
  2. Have the tool execute successfully
  3. Simulate a failure after tool execution but before the agent receives confirmation
  4. Task retries
  5. The tool fires again — duplicate side effect

Expected behavior

A retry of the same logical tool call should return the original result without re-executing the side effect.

Screenshots/Code snippets

@tool def send_payment(amount: float, recipient: str) -> str: """Send a payment to recipient""" stripe.charge(amount, recipient) # fires twice on retry return "payment sent"

Operating System

Windows 11

Python Version

3.10

crewAI Version

latest

crewAI Tools Version

lates

Virtual Environment

Venv

Evidence

See LangGraph Cloud issue #7417 for the same failure pattern documented in production: https://github.com/langchain-ai/langgraph/issues/7417

Two independent parties confirmed the exact failure mode — in-memory dedup doesn't survive worker re-dispatch, and the fix requires durable external storage keyed before execution starts.

Possible Solution

Derive a stable request_id from tool arguments before execution, claim it in durable external storage, return the cached result on any retry with the same ID. The claim must happen outside the agent execution context so a retry on a new process hits the guard immediately.

Reference implementation: SafeAgent (pip install safeagent-exec-guard) — happy to contribute a CrewAI integration example.

Additional context

Same pattern documented in LangGraph Cloud issue #7417. The fix requires durable storage outside the framework, not in-memory or graph state.