microsoft/autogen · error · ValueError

The existing handoff target {existing_message.target} is not

Error message

The existing handoff target {existing_message.target} is not one of the participants {self._participant_names}. If you are resuming Swarm with a new task make sure to include in your task a HandoffMessage with a valid participant as the target. For example, if you are resuming from a HandoffTermination, make sure the new task is a HandoffMessage with a valid participant as the target.

What it means

SwarmGroupChatManager.validate_group_state raises ValueError when the most recent HandoffMessage already in the saved message thread targets a name that is not a current participant. On resume, the swarm continues from the latest handoff, so that target must exist in the reloaded team.

Source

Thrown at python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_swarm_group_chat.py:64

    async def validate_group_state(self, messages: List[BaseChatMessage] | None) -> None:
        """Validate the start messages for the group chat."""
        # Check if any of the start messages is a handoff message.
        if messages:
            for message in messages:
                if isinstance(message, HandoffMessage):
                    if message.target not in self._participant_names:
                        raise ValueError(
                            f"The target {message.target} is not one of the participants {self._participant_names}. "
                            "If you are resuming Swarm with a new HandoffMessage make sure to set the target to a valid participant as the target."
                        )
                    return

        # Check if there is a handoff message in the thread that is not targeting a valid participant.
        for existing_message in reversed(self._message_thread):
            if isinstance(existing_message, HandoffMessage):
                if existing_message.target not in self._participant_names:
                    raise ValueError(
                        f"The existing handoff target {existing_message.target} is not one of the participants {self._participant_names}. "
                        "If you are resuming Swarm with a new task make sure to include in your task "
                        "a HandoffMessage with a valid participant as the target. For example, if you are "
                        "resuming from a HandoffTermination, make sure the new task is a HandoffMessage "
                        "with a valid participant as the target."
                    )
                # The latest handoff message should always target a valid participant.
                # Do not look past the latest handoff message.
                return

    async def reset(self) -> None:
        self._current_turn = 0
        self._message_thread.clear()
        if self._termination_condition is not None:
            await self._termination_condition.reset()
        self._current_speaker = self._participant_names[0]

    async def select_speaker(self, thread: Sequence[BaseAgentEvent | BaseChatMessage]) -> List[str] | str:

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Reconstruct the Swarm with the same participant names as when the state was saved.
  2. Alternatively, start the new task with a HandoffMessage targeting a valid participant — but the check still inspects the existing thread, so renaming participants on resume is not supported; keep names stable.
  3. If you must rename, re-serialize the thread and rewrite the latest handoff target to a valid participant name before loading state.

Example fix

# before
# state saved when participants were ['researcher', 'coder']
swarm = Swarm(participants=[researcher, "programmer"])  # renamed -> resume fails
await swarm.load_state(saved)

# after
swarm = Swarm(participants=[researcher, coder])  # keep names identical to saved state
await swarm.load_state(saved)
Defensive patterns

Strategy: validation

Validate before calling

# Before load_state: ensure participant names match those used when state was saved
saved_targets = [m.target for m in saved_thread if isinstance(m, HandoffMessage)]
assert all(t in [a.name for a in agents] for t in saved_targets)

Try / catch

try:
    await team.run(task=new_task)
except ValueError as e:
    if "existing handoff target" in str(e):
        raise ValueError("Participant names changed since state save; rebuild with original names") from e
    raise

Prevention

When it happens

Trigger: Saving a swarm state (save_state), reconstructing the team with a different/renamed participant set, then loading the state and running a new task; the thread's latest HandoffMessage targets the old name.

Common situations: Resume-from-handoff workflows where participants were renamed between sessions; serializing state in one process and resuming in another with a different roster.

Related errors


AI-assisted analysis of microsoft/autogen@027ecf0a37 (2026-08-15). Data as JSON: /api/errors/c157957a6d7bce43. Report an issue: GitHub.