How to Fix 'agent infinite loop in production' in AutoGen (TypeScript)

By Cyprian AaronsUpdated 2026-04-21
agent-infinite-loop-in-productionautogentypescript

If you’re seeing agent infinite loop in production, your AutoGen agent is almost always failing to reach a terminal state. In practice, that means the model keeps producing tool calls, retries, or self-referential replies until your runtime hits a max-iteration guard, timeout, or custom loop detector.

In AutoGen TypeScript, this usually shows up when an agent never emits a final answer, a tool result keeps re-triggering the same planner step, or your message routing sends the same event back into the same agent chain.

The Most Common Cause

The #1 cause is a missing termination condition in an agent loop. In AutoGen TS, this often happens when you keep calling run() or onMessages() without checking whether the assistant has already produced a final response.

Here’s the broken pattern:

BrokenFixed
Keeps re-invoking the agent with no exit checkStops when the assistant returns a final answer
// Broken: endless re-entry into the same agent flow
import { AssistantAgent } from "@autogen/agents";

const agent = new AssistantAgent({
  name: "support-agent",
  systemMessage: "Help the user and use tools when needed.",
});

async function handleRequest(input: string) {
  let current = input;

  // Problem: no termination condition tied to model output
  while (true) {
    const result = await agent.run([{ role: "user", content: current }]);
    current = result.messages.at(-1)?.content ?? "";
  }
}
// Fixed: stop when the agent produces a final answer
import { AssistantAgent } from "@autogen/agents";

const agent = new AssistantAgent({
  name: "support-agent",
  systemMessage: "Help the user and use tools when needed.",
});

async function handleRequest(input: string) {
  const result = await agent.run([{ role: "user", content: input }]);

  const lastMessage = result.messages.at(-1);
  if (!lastMessage) throw new Error("No assistant response returned");

  // Only continue if your app explicitly expects another turn
  return lastMessage.content;
}

If you’re using a group chat or orchestrator, the same bug appears when no speaker can ever terminate. The runtime keeps cycling because every agent thinks someone else should answer next.

Other Possible Causes

1) Your tool always returns something that triggers another tool call

A common loop is assistant -> tool -> assistant -> tool with no “final” message. This happens when the model sees tool output and decides it must call again.

// Example of a tool that encourages repeated calls
const searchTool = {
  name: "search_policy",
  description: "Search policy data",
  execute: async (query: string) => {
    return { status: "ok", data: query }; // too vague for termination
  },
};

Fix it by returning structured, complete results and telling the model when to stop.

execute: async (query: string) => {
  return {
    status: "done",
    answer: `Found policy match for ${query}`,
    shouldContinue: false,
  };
}

2) Your system prompt never defines an exit condition

If you tell the model to “keep helping until done” but never define what “done” means, it may keep trying to improve its answer forever.

const agent = new AssistantAgent({
  name: "claims-agent",
  systemMessage:
    "Keep reasoning until you are satisfied with the answer.",
});

Use explicit termination language:

const agent = new AssistantAgent({
  name: "claims-agent",
  systemMessage:
    "Answer once. If enough information is available, provide a final response and do not call tools again.",
});

3) You are feeding assistant output back as user input

This is easy to miss in production pipelines. If you append the assistant’s last message as a new user message, you create a self-amplifying loop.

// Bad: assistant output becomes new user input
messages.push({ role: "user", content: lastAssistantMessage });

Instead, preserve roles correctly:

messages.push({ role: "assistant", content: lastAssistantMessage });

4) Your max turn / recursion guard is too high or missing

AutoGen will usually protect you with limits like maxTurns, but if you override them incorrectly, you can let loops run long enough to look infinite in production logs.

const team = new RoundRobinGroupChat({
  agents,
  maxTurns: Number.MAX_SAFE_INTEGER,
});

Use sane bounds:

const team = new RoundRobinGroupChat({
  agents,
  maxTurns: 8,
});

How to Debug It

  1. Log every turn with role and message type

    • You want to see whether the loop is assistant -> tool -> assistant or assistant -> user -> assistant.
    • Log role, name, and any tool invocation metadata.
  2. Check whether any agent ever emits a terminal response

    • In AutoGen TS, inspect the final message returned by AssistantAgent.run() or your group chat result.
    • If every turn ends in a tool call, your prompt or tool contract is wrong.
  3. Lower your turn limit temporarily

    • Set maxTurns to something small like 3 or 5.
    • If it still spins within that window, you’ve got a routing bug rather than just “too many turns.”
  4. Disable tools one by one

    • Start with plain text only.
    • Re-enable each tool until the loop returns.
    • The first tool that causes repeated invocation is usually returning ambiguous output or missing completion semantics.

Prevention

  • Define explicit stop conditions in both code and prompts.
  • Keep tool outputs structured:
    • include status
    • include answer
    • include shouldContinue
  • Put hard guards in production:
    • max turns
    • timeout
    • repeated-message detection

A good production rule is simple: if an AutoGen conversation repeats the same intent twice without new data, stop it and surface an error like AutoGenRuntimeError: conversation exceeded maxTurns instead of letting it churn indefinitely. That turns an invisible loop into an observable failure you can fix fast.


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