How to Fix 'duplicate tool calls' in AutoGen (TypeScript)

By Cyprian AaronsUpdated 2026-04-21
duplicate-tool-callsautogentypescript

If you’re seeing duplicate tool calls in AutoGen TypeScript, the model is trying to invoke the same tool call more than once in a single turn, or your app is re-processing the same assistant message twice. In practice, this usually shows up when you mix manual tool execution with AutoGen’s built-in tool handling, or when your message loop is replaying state incorrectly.

The error often looks like this:

Error: duplicate tool calls detected for message id=...

or you’ll see AutoGen reject an assistant response that contains repeated tool_calls entries for the same turn.

The Most Common Cause

The #1 cause is handling tool calls manually and also letting AutoGen handle them automatically. In TypeScript, this happens when you read assistantMessage.toolCalls, execute the tools yourself, then pass the same message back into AssistantAgent / RoundRobinGroupChat / your own loop without clearing or advancing state correctly.

Wrong pattern vs right pattern

BrokenFixed
Manually executes tools and re-feeds the same assistant messageLet AutoGen own the tool-call lifecycle, or only execute once
Same tool_calls payload processed twiceEach assistant message is consumed exactly once
// ❌ WRONG: manual tool execution + replaying the same assistant message
const response = await agent.run(task);

for (const msg of response.messages) {
  if (msg.role === "assistant" && msg.toolCalls?.length) {
    for (const call of msg.toolCalls) {
      const result = await myTools[call.name](call.arguments);
      // Feeding results back without advancing state correctly
      await agent.send({
        role: "tool",
        toolCallId: call.id,
        content: JSON.stringify(result),
      });
    }
  }
}

// Later, the same assistant message gets processed again:
await agent.run(task);
// ✅ RIGHT: let AutoGen manage tool calls in the normal agent loop
const agent = new AssistantAgent({
  name: "support-agent",
  modelClient,
  tools: [lookupPolicyTool, getClaimStatusTool],
});

const result = await agent.run("Check claim status for policy 12345");
console.log(result.messages);

If you need manual control, do it in one place only. Don’t both intercept toolCalls and also register the same functions in tools.

Other Possible Causes

1) You are replaying persisted messages from storage

If you save conversation history and then reload it on every request, you can accidentally resend an assistant message that already contained tool calls.

// ❌ Reusing old messages without deduping
const history = await db.getConversation(conversationId);
await agent.run({ messages: history });

// ✅ Only append new user input, keep prior state consistent
const history = await db.getConversation(conversationId);
history.push({ role: "user", content: input });
await agent.run({ messages: history });

2) Your streaming handler processes partial events twice

With streaming, it’s easy to handle a delta event and then handle the final assembled message again. That can trigger duplicate tool execution if your code listens to both.

// ❌ Handling both delta and final completion as if they were separate turns
stream.on("messageDelta", handleToolCallDelta);
stream.on("messageComplete", handleToolCallDelta);

// ✅ Only execute tools from one canonical event path
stream.on("messageComplete", async (msg) => {
  if (msg.toolCalls?.length) await runToolsOnce(msg);
});

3) Your custom tool wrapper registers duplicates

Sometimes the issue is not the agent loop; it’s your tool registry. If two wrappers expose the same function name, AutoGen may surface duplicate calls or ambiguous routing.

// ❌ Same logical tool registered twice under different wrappers
const tools = [
  createTool(lookupPolicy),
  createTool(lookupPolicy), // duplicate registration
];

// ✅ Register each tool once with a unique name
const tools = [
  createTool({ name: "lookupPolicy", handler: lookupPolicy }),
];

4) The model is being retried without idempotency

If a request times out and you retry at the HTTP layer, the first attempt may have already produced a tool call. Retrying blindly can cause the same call to be processed twice.

// ❌ Blind retry with no request ID / idempotency key
await retry(() => agent.run(input));

// ✅ Track request IDs and dedupe at your boundary
await retry(() => agent.run({ input, metadata: { requestId } }));

How to Debug It

  1. Log every assistant message before executing tools

    • Print message.id, role, content, and toolCalls.
    • If you see the same message.id twice, your loop is replaying state.
  2. Check whether tools are registered twice

    • Search for repeated imports, wrapper factories, or merged arrays.
    • Verify that each logical function name appears once in AssistantAgent.tools.
  3. Inspect whether streaming and non-streaming paths both run

    • A common bug is calling agent.run() and also listening to stream callbacks on the same request.
    • Pick one path per request.
  4. Add a per-message dedupe guard

    • Store processed toolCall.id values in a Set.
    • If a call ID repeats, stop and inspect where it was re-emitted.
const seenToolCalls = new Set<string>();

function shouldProcess(callId: string) {
  if (seenToolCalls.has(callId)) return false;
  seenToolCalls.add(callId);
  return true;
}

Prevention

  • Keep one owner for tool execution:
    • either AutoGen handles tools end-to-end,
    • or your orchestration layer does.
  • Make retries idempotent:
    • include request IDs,
    • dedupe by toolCall.id,
    • never replay full assistant turns blindly.
  • Add structured logging around:
    • agent turn ID,
    • message ID,
    • tool call ID,
    • tool name.

If you’re building production agents for banking or insurance workflows, this matters more than it looks. Duplicate tool calls can mean duplicated side effects: repeated policy lookups, duplicate claim updates, or double writes to downstream systems.


Keep learning

By Cyprian Aarons, AI Consultant at Topiax.

Want the complete 8-step roadmap?

Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.

Get the Starter Kit

Related Guides