Describe the bug
DESCRIPTION
When using AutoPattern with set_after_work(TerminateTarget()) on an agent, the termination signal is silently ignored. The workflow runs to max_rounds instead of stopping after the agent speaks.
SEVERITY - HIGH 🔴
TerminateTarget is a core API primitive. It silently does nothing instead of failing visibly, making the bug hard to diagnose.
MITIGATIONS
The workaround found consists in a custom is_termination_msg callback on the GroupChatManager via group_manager_args, which bypasses the after_work mechanism entirely.
Steps to reproduce
AG2 version: 0.11.4
python
from autogen import ConversableAgent
from autogen.agentchat.group.patterns import AutoPattern
from autogen.agentchat.group.multi_agent_chat import initiate_group_chat
from autogen.agentchat.group.targets.transition_target import TerminateTarget
llm_config = {"model": "factory-model_name-version-release", "api_type": "factory"}
agent_a = ConversableAgent(
name="agent_a",
system_message="Say hello in one sentence.",
llm_config=llm_config,
human_input_mode="NEVER",
)
agent_b = ConversableAgent(
name="agent_b",
system_message="Say goodbye in one sentence.",
llm_config=llm_config,
human_input_mode="NEVER",
)
# agent_b should terminate the workflow after speaking
agent_b.handoffs.set_after_work(TerminateTarget())
user = ConversableAgent(name="user", llm_config=None, human_input_mode="TERMINATE")
pattern = AutoPattern(
initial_agent=agent_a,
agents=[agent_a, agent_b],
user_agent=user,
group_manager_args={"llm_config": llm_config},
)
result, ctx, last = initiate_group_chat(
pattern=pattern,
messages="Start the conversation.",
max_rounds=10,
)
# Check how many rounds actually ran
print(f"Messages: {len(result.chat_history)}")
# Expected: ~3-4 (user prompt, agent_a, agent_b, done)
# Actual: runs to max_rounds=10
Model Used
claude-sonnet-4-5-20250929 - claude-haiku-4-5-20251001
Expected Behavior
After agent_b speaks, TerminateTarget should stop the workflow. The chat history should contain ~3-4 messages.
Screenshots and logs
No response
Additional Information
SUGGESTED FIX
Use a sentinel to distinguish "no after_work configured" from "terminate":
python
_NO_AFTER_WORK = object()
def _evaluate_after_works_conditions(...):
if not hasattr(agent, "handoffs") or not agent.handoffs.after_works:
return _NO_AFTER_WORK # ← sentinel instead of None
# ... rest unchanged ...
# In determine_next_agent:
after_works_result = _evaluate_after_works_conditions(...)
if after_works_result is not _NO_AFTER_WORK:
return after_works_result # None (terminate) passes through correctly
This lets None from TerminateTarget propagate to group_transition() → _prepare_and_select_agents() → NoEligibleSpeakerError → clean termination.
NOTE: OnContextCondition with AgentNameTarget works correctly because it resolves to a non-None agent reference. Only TerminateTarget is affected because its termination signal maps to None.
Describe the bug
DESCRIPTION
When using
AutoPatternwithset_after_work(TerminateTarget())on an agent, the termination signal is silently ignored. The workflow runs tomax_roundsinstead of stopping after the agent speaks.SEVERITY - HIGH 🔴
TerminateTargetis a core API primitive. It silently does nothing instead of failing visibly, making the bug hard to diagnose.MITIGATIONS
The workaround found consists in a custom
is_termination_msgcallback on theGroupChatManagerviagroup_manager_args, which bypasses the after_work mechanism entirely.Steps to reproduce
AG2 version: 0.11.4
python
Model Used
claude-sonnet-4-5-20250929-claude-haiku-4-5-20251001Expected Behavior
After
agent_bspeaks,TerminateTargetshould stop the workflow. The chat history should contain ~3-4 messages.Screenshots and logs
No response
Additional Information
SUGGESTED FIX
Use a sentinel to distinguish "no after_work configured" from "terminate":
python
This lets
NonefromTerminateTargetpropagate togroup_transition()→_prepare_and_select_agents()→NoEligibleSpeakerError→ clean termination.NOTE:
OnContextConditionwithAgentNameTargetworks correctly because it resolves to a non-Noneagent reference. OnlyTerminateTargetis affected because its termination signal maps toNone.