How to Fix 'agent infinite loop' in AutoGen (TypeScript)
What the error means
agent infinite loop in AutoGen usually means your agents keep producing messages, but nothing in the conversation ever satisfies the termination condition. In TypeScript, this often shows up when you wire an AssistantAgent and UserProxyAgent together, but forget to stop on a final answer, tool result, or max turn limit.
The pattern is simple: the conversation keeps bouncing between agents until AutoGen gives up or your app hangs.
The Most Common Cause
The #1 cause is a missing or broken termination condition. In AutoGen TypeScript, this usually happens when isTerminationMessage never returns true, or your agent always replies even after it has already answered the question.
Here’s the broken pattern:
import { AssistantAgent, UserProxyAgent } from "@autogen/agentchat";
const assistant = new AssistantAgent({
name: "assistant",
llmConfig: { model: "gpt-4o-mini" },
});
const user = new UserProxyAgent({
name: "user",
humanInputMode: "NEVER",
});
await user.initiateChat(assistant, {
message: "Summarize this invoice.",
maxRound: 20,
});
And here’s what is happening:
| Broken | Fixed |
|---|---|
| No explicit termination rule | Stop when the assistant produces a final answer |
maxRound is high enough to hide the bug | Use a real stop condition, not just a timeout |
| Agent keeps responding forever | Conversation ends on TERMINATE or equivalent |
Fixed version:
import { AssistantAgent, UserProxyAgent } from "@autogen/agentchat";
const assistant = new AssistantAgent({
name: "assistant",
llmConfig: { model: "gpt-4o-mini" },
});
const user = new UserProxyAgent({
name: "user",
humanInputMode: "NEVER",
isTerminationMessage: (msg) => {
return typeof msg.content === "string" && msg.content.includes("TERMINATE");
},
});
await user.initiateChat(assistant, {
message: "Summarize this invoice and end with TERMINATE.",
maxRound: 10,
});
If your agent framework version uses a different hook name, the fix is the same: define one clear exit path and make both sides respect it.
Other Possible Causes
1. Tool calls never resolve
If a tool call throws, returns malformed data, or never resolves, the assistant often retries the same action and looks stuck in a loop.
// Broken
const tools = [{
name: "lookupPolicy",
description: "Fetch policy details",
execute: async () => {
throw new Error("DB timeout");
},
}];
Fix it by returning structured failures and teaching the assistant to stop retrying:
// Fixed
const tools = [{
name: "lookupPolicy",
description: "Fetch policy details",
execute: async () => {
try {
return { ok: true, data: await fetchPolicy() };
} catch (err) {
return { ok: false, error: "DB timeout", retryable: false };
}
},
}];
2. The system prompt encourages endless refinement
A prompt like “keep improving until perfect” can make the model keep revising instead of finishing.
// Broken
systemMessage:
"Review the claim response and keep refining it until it is as good as possible."
Use an explicit completion rule:
// Fixed
systemMessage:
"Review the claim response once. If it meets requirements, output final answer and end with TERMINATE."
3. Message history is being replayed incorrectly
If you append the same assistant output back into history on every turn, AutoGen will see repeated context and may keep generating similar replies.
// Broken
history.push(lastAssistantMessage);
history.push(lastAssistantMessage); // duplicated by accident
Make sure each turn adds exactly one new message per speaker.
4. Max turns are too high and masking a logic bug
A large maxRound does not fix a loop. It only delays failure.
// Broken
await user.initiateChat(assistant, {
message: "Process this request.",
maxRound: 100,
});
Lower it while debugging:
// Fixed
await user.initiateChat(assistant, {
message: "Process this request.",
maxRound: 5,
});
If it still loops at five turns, your stop condition is wrong.
How to Debug It
- •
Print every message in the conversation
- •Log role, content, and whether a tool was called.
- •You want to see which message repeats.
- •
Check termination logic first
- •Confirm
isTerminationMessage, stop tokens, or final-answer rules are actually reachable. - •If your assistant never emits
TERMINATE, the chat will not end.
- •Confirm
- •
Disable tools temporarily
- •Run the same conversation with tools removed.
- •If looping stops, your tool handler is returning bad output or throwing repeatedly.
- •
Reduce
maxRoundand simplify prompts- •Set
maxRoundto something small like3or5. - •Replace complex prompts with a single instruction:
- •“Answer once and stop.”
- •If that works, add complexity back one piece at a time.
- •Set
Prevention
- •Always define one explicit termination contract:
- •A token like
TERMINATE - •A final-answer schema field like
{ done: true }
- •A token like
- •Treat tool outputs as API responses:
- •Return structured success/failure objects
- •Never let exceptions bubble into silent retries
- •Add chat-level tests:
- •One test for normal completion
- •One test for tool failure
- •One test for repeated-agent-message detection
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
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