Skip to content

Python: [Bug]: "Duplicate item found" error when using AzureAIClient with tools in workflows #3295

Closed

Description

@firrasaltaher

Description

Summary

When using AzureAIClient with agents that have tools in a workflow, the Azure AI API returns a 400 error: "Duplicate item found with id fc_xxxxx. Remove duplicate items from your input and try again." This prevents workflows with tool-enabled agents from executing.

Environment

  • agent-framework version: 1.0.0b260116
  • agent-framework-azure-ai version: 1.0.0b260116
  • agent-framework-core version: 1.0.0b260116
  • Python version: 3.12.11
  • OS: Windows 11
  • Azure AI API version: 2025-11-15-preview
  • Date: January 19, 2026

Steps to Reproduce

  1. Create a workflow with multiple agents using AzureAIClient
  2. Configure at least one agent with tools using the tools= parameter in as_agent()
  3. Run the workflow with workflow.run_stream(query)
  4. Observe the error when the agent with tools attempts to execute

Minimal Reproduction Code

import asyncio
import os
from dotenv import load_dotenv
from azure.ai.projects.aio import AIProjectClient
from azure.identity.aio import DefaultAzureCredential
from agent_framework import ai_function, WorkflowBuilder, executor, WorkflowContext, ChatMessage
from agent_framework.azure import AzureAIClient

load_dotenv()

# Define a simple tool
@ai_function(name="search_hotels", description="Search for hotels")
def search_hotels(location: str) -> str:
    return f"Mock hotel results for {location}"

@executor(id="start")
async def start_executor(input: str, ctx: WorkflowContext[str, str]) -> None:
    await ctx.send_message([ChatMessage(role="user", text=input)])

async def main():
    project_client = AIProjectClient(
        endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
        credential=DefaultAzureCredential(),
        api_version="2025-11-15-preview",
    )

    async with (
        DefaultAzureCredential() as credential,
        project_client,
        AzureAIClient(project_client=project_client, credential=credential) as client,
    ):
        # Create agent with tools
        hotel_search_client = AzureAIClient(
            project_client=project_client, 
            credential=credential, 
            agent_name="hotel-agent"
        )
        hotel_agent = hotel_search_client.as_agent(
            id="hotel-agent",
            instructions="Search for hotels using the search_hotels tool.",
            name="hotel-agent",
            tools=[search_hotels],  # ← This causes the bug
            store=True,
        )

        # Build simple workflow
        workflow = (
            WorkflowBuilder()
            .set_start_executor(start_executor)
            .add_edge(start_executor, hotel_agent)
            .build()
        )

        # Run workflow - this will fail
        async for event in workflow.run_stream("Find hotels in Paris"):
            print(event)

if __name__ == "__main__":
    asyncio.run(main())

Expected Behavior

The workflow should execute successfully, with the agent calling the search_hotels tool and returning results based on the tool output.

Actual Behavior

The workflow fails with the following error:

Error during workflow execution: <class 'agent_framework_azure_ai._client.AzureAIClient'> service failed to complete the prompt: Error code: 400 - {'error': {'message': 'Duplicate item found with id fc_047d0ef380cbbb9f00696f01af725881959ee4e2494b72def8. Remove duplicate items from your input and try again.', 'type': 'invalid_request_error', 'param': 'input', 'code': None}}

Traceback (most recent call last):
  File "agent_framework\openai\_responses_client.py", line 266, in _inner_get_streaming_response
    async for chunk in await client.responses.create(stream=True, **run_options):
  ...
openai.BadRequestError: Error code: 400 - {'error': {'message': 'Duplicate item found with id fc_047d0ef380cbbb9f00696f01af725881959ee4e2494b72def8. Remove duplicate items from your input and try again.', 'type': 'invalid_request_error', 'param': 'input', 'code': None}}

The error indicates that function call items with the same ID are being duplicated in the API request payload, which the Azure AI Responses API rejects.

Code Sample

Run the sample: https://github.com/microsoft/agent-framework/tree/main/python/samples/demos/workflow_evaluation

or

### Minimal Reproduction Code


import asyncio
import os
from dotenv import load_dotenv
from azure.ai.projects.aio import AIProjectClient
from azure.identity.aio import DefaultAzureCredential
from agent_framework import ai_function, WorkflowBuilder, executor, WorkflowContext, ChatMessage
from agent_framework.azure import AzureAIClient

load_dotenv()

# Define a simple tool
@ai_function(name="search_hotels", description="Search for hotels")
def search_hotels(location: str) -> str:
    return f"Mock hotel results for {location}"

@executor(id="start")
async def start_executor(input: str, ctx: WorkflowContext[str, str]) -> None:
    await ctx.send_message([ChatMessage(role="user", text=input)])

async def main():
    project_client = AIProjectClient(
        endpoint=os.environ["AZURE_AI_PROJECT_ENDPOINT"],
        credential=DefaultAzureCredential(),
        api_version="2025-11-15-preview",
    )

    async with (
        DefaultAzureCredential() as credential,
        project_client,
        AzureAIClient(project_client=project_client, credential=credential) as client,
    ):
        # Create agent with tools
        hotel_search_client = AzureAIClient(
            project_client=project_client, 
            credential=credential, 
            agent_name="hotel-agent"
        )
        hotel_agent = hotel_search_client.as_agent(
            id="hotel-agent",
            instructions="Search for hotels using the search_hotels tool.",
            name="hotel-agent",
            tools=[search_hotels],  # ← This causes the bug
            store=True,
        )

        # Build simple workflow
        workflow = (
            WorkflowBuilder()
            .set_start_executor(start_executor)
            .add_edge(start_executor, hotel_agent)
            .build()
        )

        # Run workflow - this will fail
        async for event in workflow.run_stream("Find hotels in Paris"):
            print(event)

if __name__ == "__main__":
    asyncio.run(main())

Error Messages / Stack Traces

The workflow fails with the following error:


Error during workflow execution: <class 'agent_framework_azure_ai._client.AzureAIClient'> service failed to complete the prompt: Error code: 400 - {'error': {'message': 'Duplicate item found with id fc_047d0ef380cbbb9f00696f01af725881959ee4e2494b72def8. Remove duplicate items from your input and try again.', 'type': 'invalid_request_error', 'param': 'input', 'code': None}}

Traceback (most recent call last):
  File "agent_framework\openai\_responses_client.py", line 266, in _inner_get_streaming_response
    async for chunk in await client.responses.create(stream=True, **run_options):
  ...
openai.BadRequestError: Error code: 400 - {'error': {'message': 'Duplicate item found with id fc_047d0ef380cbbb9f00696f01af725881959ee4e2494b72def8. Remove duplicate items from your input and try again.', 'type': 'invalid_request_error', 'param': 'input', 'code': None}}


The error indicates that function call items with the same ID are being duplicated in the API request payload, which the Azure AI Responses API rejects.

Package Versions

agent-framework version: 1.0.0b260116, agent-framework-azure-ai version: 1.0.0b260116, agent-framework-core version: 1.0.0b260116

Python Version

No response

Additional Context

What Works

  • ✅ Workflows with AzureAIClient agents WITHOUT tools work perfectly
  • ✅ Single agents (non-workflow) with AzureAIClient and tools work
  • ✅ The official sample at python/samples/demos/workflow_evaluation/ has the same structure but appears to have the same issue

What Doesn't Work

  • ❌ Multi-agent workflows with AzureAIClient where agents have the tools= parameter
  • ❌ Both fan-out and sequential workflow patterns exhibit this issue
  • ❌ Error occurs consistently across different tool configurations

Workaround

Remove the tools= parameter from agents in workflows and have them use their knowledge instead:

# Instead of:
hotel_agent = client.as_agent(
    id="hotel-agent",
    instructions="Use search_hotels to find hotels.",
    tools=[search_hotels],  # ← Remove this
    store=True,
)

# Use:
hotel_agent = client.as_agent(
    id="hotel-agent",
    instructions="Based on the request, suggest hotels using your knowledge.",
    # No tools parameter
    store=True,
)

Related Code References

  • Official sample with same pattern: python/samples/demos/workflow_evaluation/create_workflow.py
  • Error occurs in: agent_framework/openai/_responses_client.py, line 266
  • Related to Azure AI Responses API tool handling in workflow contexts

Suspected Root Cause

The agent framework appears to be sending duplicate function call items in the request payload when:

  1. Multiple agents in a workflow have tools
  2. The workflow uses fan-out or sequential patterns
  3. Message history is being passed between agents

This could be related to how tool definitions or function call tracking is handled across workflow agent boundaries in the AzureAIClient implementation.

Impact

  • Severity: High - Blocks using tools in multi-agent workflows with AzureAIClient
  • Scope: Affects Azure AI Foundry integration (AzureAIClient) specifically
  • Frequency: 100% reproduction rate when tools are used in workflows

Suggested Fix Areas

  1. Review how AzureAIClient constructs the Responses API payload for agents with tools
  2. Check for duplicate function call ID tracking across workflow agent invocations
  3. Verify message history sanitization when passing between workflow agents
  4. Ensure tool definitions aren't being registered multiple times per workflow execution

Additional Files

  • Full reproduction case: create_workflow.py (with tools - fails)
  • Working workaround: create_workflow_no_tools.py (without tools - succeeds)
  • Tool definitions: _tools.py

Activity

  1. added
    bugUsage: [Issues], Target: all issues (Legacy, prefer issue type: bug)
    on Jan 20, 2026
  2. added
    workflowsUsage: [Issues, PRs], Target: Workflows
    and removed
    triageUsage: [Issues], Target: All issues that still need to be triaged
    on Jan 20, 2026
  3. vinhnguyen-wareflex commented on Jan 27, 2026

    @vinhnguyen-wareflex

    I have the same issue. Is there any workaround for this error?

  4. praneet22 commented on Feb 11, 2026

    @praneet22

    The issue seems to be when using openai responses api, as it saves the state by default. When using chat completion api, I do not see this issue. Team need to expose the store parm in the openai client, which we can set to False and should fix this issue.

    response = client.responses.create(
    model="gpt-4o-mini",
    input="tell me a joke",
    store=False, # Set store to False
    )

  5. pamelafox commented on Apr 3, 2026

    @pamelafox
    Member

    We hit this same issue using OpenAIChatClient (not AzureAIClient) with the built-in RedisHistoryProvider in MAF 1.0.0.

    Setup: A simple single-agent conversation (not a workflow) with tools and RedisHistoryProvider for multi-turn persistence.

    agent = Agent(
        client=OpenAIChatClient(...),
        instructions="You are a helpful weather agent.",
        tools=[get_weather],
        context_providers=[RedisHistoryProvider(source_id="redis_chat", redis_url=REDIS_URL)],
    )
    session = agent.create_session(session_id=session_id)
    
    # Turn 1 — works fine, tool gets called
    response = await agent.run("What's the weather in Tokyo?", session=session)
    
    # Turn 2 — fails with "Duplicate item found with id rs_..."
    response = await agent.run("How about Paris?", session=session)

    What happens: Turn 1 succeeds and the tool call completes. The history provider saves the response messages, which contain rs_*-prefixed item IDs (from the Responses API output) in additional_properties (fc_id). On Turn 2, the history provider replays those saved messages as input, and the API rejects them as duplicates.

    We also tested store=False as Praneet Solanki (@praneet22) suggested:

    • default_options={"store": False} on the Agent works for text-only conversations (no tools). The official Cosmos history provider sample uses this pattern.
    • However, with tools it fails differently: the first turn's tool call loop itself breaks because the tool result references an rs_* function call ID that was never persisted server-side:
      "Item with id 'rs_...' not found. Items are not persisted when store is set
      to false. Try again with store set to true, or remove this item from your input."
      

    So store=False doesn't help when tools are involved — the tool call/result cycle within a single turn relies on store=True.

    Root cause: Same as the original report — Responses API item IDs (fc_* / rs_*) from tool call outputs get embedded in saved messages and replayed, causing either "Duplicate item" (store=True) or "Item not found" (store=False).

    Workaround: Remove tools= from agents that use RedisHistoryProvider. Text-only agents with history providers work fine.

    Environment: agent-framework 1.0.0, agent-framework-openai 1.0.0, Python 3.12, Azure OpenAI (gpt-5-mini)

  6. TaoChenOSU commented on Apr 9, 2026

    @TaoChenOSU
    Contributor

    I am no longer able to reproduce this error. After upgrading to 1.0, could you try the following:

    import asyncio
    
    from agent_framework import (
        Agent,
        AgentResponseUpdate,
        Message,
        WorkflowBuilder,
        WorkflowContext,
        executor,
        tool,
    )
    from agent_framework.openai import OpenAIChatClient
    from dotenv import load_dotenv
    
    load_dotenv()
    
    
    # Define a simple tool
    @tool(name="search_hotels", description="Search for hotels")
    def search_hotels(location: str) -> str:
        return f"Mock hotel results for {location}"
    
    
    @executor(id="start")
    async def start_executor(input: str, ctx: WorkflowContext[list[Message]]) -> None:
        await ctx.send_message([Message(role="user", contents=[input])])
    
    
    async def main():
        client = OpenAIChatClient()
    
        hotel_agent = Agent(
            client=client,
            id="hotel-agent",
            instructions="Search for hotels using the search_hotels tool.",
            name="hotel-agent",
            tools=[search_hotels],  # ← This causes the bug
        )
    
        # Build simple workflow
        workflow = WorkflowBuilder(start_executor=start_executor).add_edge(start_executor, hotel_agent).build()
    
        # Run workflow - this will fail
        # Track the last author to format streaming output.
        last_author: str | None = None
        async for event in workflow.run("Find hotels in Paris", stream=True):
            if event.type == "output" and isinstance(event.data, AgentResponseUpdate):
                update = event.data
                author = update.author_name
                if author != last_author:
                    if last_author is not None:
                        print()  # Newline between different authors
                    print(f"{author}: {update.text}", end="", flush=True)
                    last_author = author
                else:
                    print(update.text, end="", flush=True)
    
    
    if __name__ == "__main__":
        asyncio.run(main())
  7. CristinaStn commented on Apr 21, 2026

    @CristinaStn

    Hi Tao Chen (@TaoChenOSU), do you have any resolution for Pamela Fox (@pamelafox) situation?
    I am facing the same issue using CosmosHistoryProvider. Initialli discovered the bug using 1.0.0, just upgraded to agent-framework 1.1.0 and issue is replicated.

    Here is my code snapshot in case you need additional insights:

    @tool
    def get_weather(city: str) -> str:
        """Get weather for a city."""
        return f"The weather in {city} is sunny and 22°C"
    
    async def main() -> None:
        """Run the messages history sample."""
        project_endpoint = os.getenv("FOUNDRY_PROJECT_ENDPOINT")
        model = os.getenv("FOUNDRY_MODEL")
        cosmos_endpoint = os.getenv("AZURE_COSMOS_ENDPOINT")
        cosmos_database_name = os.getenv("AZURE_COSMOS_DATABASE_NAME")
        cosmos_container_name = os.getenv("AZURE_COSMOS_CONTAINER_NAME")
        cosmos_key = os.getenv("AZURE_COSMOS_KEY")
    
        if (
            not project_endpoint
            or not model
            or not cosmos_endpoint
            or not cosmos_database_name
            or not cosmos_container_name
        ):
            print(
                "Please set FOUNDRY_PROJECT_ENDPOINT, FOUNDRY_MODEL, "
                "AZURE_COSMOS_ENDPOINT, AZURE_COSMOS_DATABASE_NAME, and AZURE_COSMOS_CONTAINER_NAME."
            )
            return
    
        async with (
            AzureCliCredential() as credential,
            CosmosHistoryProvider(
                endpoint=cosmos_endpoint,
                database_name=cosmos_database_name,
                container_name=cosmos_container_name,
                credential=cosmos_key or credential,
            ) as history_provider,
            Agent(
                client=FoundryChatClient(
                    project_endpoint=project_endpoint,
                    model=model,
                    credential=credential,
                ),
                name="HistoryAgent",
                instructions="You are a helpful assistant that remembers prior turns.",
                tools=[get_weather],
                context_providers=[history_provider],
                default_options={"store": True},
            ) as agent,
        ):
            session = agent.create_session()
            session_id = session.session_id
    
            # 1. Have a multi-turn conversation.
            print(f"=== Building a conversation with session id: {session_id}===\n")
    
            queries = [
                "What is weather in Paris?",
                "Hi! My favorite programming language is Python.",
                "I also enjoy hiking in the mountains on weekends.",
                "What do you know about me so far?",
    
            ]
    
            for query in queries:
                print(f"User:      {query}")
                response = await agent.run(query, session=session)

    Output Logs:

    User:      What is weather in Paris?
    Assistant: It’s sunny and about 22°C in Paris.
    User:      Hi! My favorite programming language is Python.
    agent_framework.exceptions.ChatClientException: <class 'agent_framework_foundry._chat_client.FoundryChatClient'> service failed to complete the prompt: Error code: 400 - {'error': {'message': 'Duplicate item found with id rs_0494f362095506150069e72dd8ef2c8193b6890a96f96fbbff. Remove duplicate items from your input and try again.', 'type': 'invalid_request_error', 'param': 'input', 'code': None}}
    

    We hit this same issue using OpenAIChatClient (not AzureAIClient) with the built-in RedisHistoryProvider in MAF 1.0.0.

    Setup: A simple single-agent conversation (not a workflow) with tools and RedisHistoryProvider for multi-turn persistence.

    agent = Agent(
    client=OpenAIChatClient(...),
    instructions="You are a helpful weather agent.",
    tools=[get_weather],
    context_providers=[RedisHistoryProvider(source_id="redis_chat", redis_url=REDIS_URL)],
    )
    session = agent.create_session(session_id=session_id)

    Turn 1 — works fine, tool gets called

    response = await agent.run("What's the weather in Tokyo?", session=session)

    Turn 2 — fails with "Duplicate item found with id rs_..."

    response = await agent.run("How about Paris?", session=session)
    What happens: Turn 1 succeeds and the tool call completes. The history provider saves the response messages, which contain rs_*-prefixed item IDs (from the Responses API output) in additional_properties (fc_id). On Turn 2, the history provider replays those saved messages as input, and the API rejects them as duplicates.

    We also tested store=False as @praneet22 suggested:

    • default_options={"store": False} on the Agent works for text-only conversations (no tools). The official Cosmos history provider sample uses this pattern.
    • However, with tools it fails differently: the first turn's tool call loop itself breaks because the tool result references an rs_* function call ID that was never persisted server-side:
      "Item with id 'rs_...' not found. Items are not persisted when store is set
      to false. Try again with store set to true, or remove this item from your input."
      

    So store=False doesn't help when tools are involved — the tool call/result cycle within a single turn relies on store=True.

    Root cause: Same as the original report — Responses API item IDs (fc_* / rs_*) from tool call outputs get embedded in saved messages and replayed, causing either "Duplicate item" (store=True) or "Item not found" (store=False).

    Workaround: Remove tools= from agents that use RedisHistoryProvider. Text-only agents with history providers work fine.

    Environment: agent-framework 1.0.0, agent-framework-openai 1.0.0, Python 3.12, Azure OpenAI (gpt-5-mini)

  8. moved this from Planned to In Review in Agent Frameworkon May 7, 2026
  9. moved this from In Review to Done in Agent Frameworkon May 13, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Labels

bugUsage: [Issues], Target: all issues (Legacy, prefer issue type: bug)pythonUsage: [Issues, PRs], Target: PythonworkflowsUsage: [Issues, PRs], Target: Workflows

Type

Projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions