microsoft/autogen · error · ValueError

Candidate function must return a non-empty list of participa

Error message

Candidate function must return a non-empty list of participant names.

What it means

SelectorGroupChatManager raises ValueError when a user-supplied candidate_func returns an empty list. The candidate function narrows who may speak next; an empty candidate set leaves the manager no one to select, so it fails fast rather than silently stalling.

Source

Thrown at python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_selector_group_chat.py:188

            if speaker is not None:
                if speaker not in self._participant_names:
                    raise ValueError(
                        f"Selector function returned an invalid speaker name: {speaker}. "
                        f"Expected one of: {self._participant_names}."
                    )
                # Skip the model based selection.
                return [speaker]

        # Use the candidate function to filter participants if provided
        if self._candidate_func is not None:
            if self._is_candidate_func_async:
                async_candidate_func = cast(AsyncCandidateFunc, self._candidate_func)
                participants = await async_candidate_func(thread)
            else:
                sync_candidate_func = cast(SyncCandidateFunc, self._candidate_func)
                participants = sync_candidate_func(thread)
            if not participants:
                raise ValueError("Candidate function must return a non-empty list of participant names.")
            if not all(p in self._participant_names for p in participants):
                raise ValueError(
                    f"Candidate function returned invalid participant names: {participants}. "
                    f"Expected one of: {self._participant_names}."
                )
        else:
            # Construct the candidate agent list to be selected from, skip the previous speaker if not allowed.
            if self._previous_speaker is not None and not self._allow_repeated_speaker:
                participants = [p for p in self._participant_names if p != self._previous_speaker]
            else:
                participants = list(self._participant_names)

        assert len(participants) > 0

        # Construct agent roles.
        # Each agent sould appear on a single line.
        roles = ""
        for topic_type, description in zip(self._participant_names, self._participant_descriptions, strict=True):

View on GitHub (pinned to 027ecf0a37)

Solutions

  1. Guarantee a non-empty fallback in the candidate function: return filtered or self._participant_names (i.e. fall back to all participants).
  2. Return None instead of [] if you want the default candidate set (the code only uses the function when it returns a list; ensure every branch returns a non-empty list).
  3. Log the thread state inside candidate_func when it returns empty to find which condition over-filters.

Example fix

# before
def candidates(thread):
    return [p for p in names if p != thread[-1].source]  # empty when only 1 participant

# after
def candidates(thread):
    filtered = [p for p in names if p != thread[-1].source]
    return filtered if filtered else list(names)  # never empty
Defensive patterns

Strategy: validation

Validate before calling

def candidates(thread):
    result = [p for p in participant_names if condition(thread, p)]
    return result if result else list(participant_names)  # never empty

Type guard

def is_nonempty_name_list(value: object) -> bool:
    return isinstance(value, list) and len(value) > 0 and all(isinstance(x, str) for x in value)

Prevention

When it happens

Trigger: candidate_func(thread) filters participants and the filter matches nothing, e.g. [p for p in names if some_condition] where the condition is false for all; the function returns [] unconditionally on some code path.

Common situations: Round-robin-style candidate functions that exclude the previous speaker even when only one participant exists; conditions based on message source that never match the current thread state.

Related errors


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