{"record":{"id":"d73fc1cc1b9c4c43","repo":"microsoft/autogen","slug":"candidate-function-must-return-a-non-empty-list-of","errorCode":null,"errorMessage":"Candidate function must return a non-empty list of participant names.","messagePattern":"Candidate function must return a non-empty list of participant names\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_selector_group_chat.py","lineNumber":188,"sourceCode":"            if speaker is not None:\n                if speaker not in self._participant_names:\n                    raise ValueError(\n                        f\"Selector function returned an invalid speaker name: {speaker}. \"\n                        f\"Expected one of: {self._participant_names}.\"\n                    )\n                # Skip the model based selection.\n                return [speaker]\n\n        # Use the candidate function to filter participants if provided\n        if self._candidate_func is not None:\n            if self._is_candidate_func_async:\n                async_candidate_func = cast(AsyncCandidateFunc, self._candidate_func)\n                participants = await async_candidate_func(thread)\n            else:\n                sync_candidate_func = cast(SyncCandidateFunc, self._candidate_func)\n                participants = sync_candidate_func(thread)\n            if not participants:\n                raise ValueError(\"Candidate function must return a non-empty list of participant names.\")\n            if not all(p in self._participant_names for p in participants):\n                raise ValueError(\n                    f\"Candidate function returned invalid participant names: {participants}. \"\n                    f\"Expected one of: {self._participant_names}.\"\n                )\n        else:\n            # Construct the candidate agent list to be selected from, skip the previous speaker if not allowed.\n            if self._previous_speaker is not None and not self._allow_repeated_speaker:\n                participants = [p for p in self._participant_names if p != self._previous_speaker]\n            else:\n                participants = list(self._participant_names)\n\n        assert len(participants) > 0\n\n        # Construct agent roles.\n        # Each agent sould appear on a single line.\n        roles = \"\"\n        for topic_type, description in zip(self._participant_names, self._participant_descriptions, strict=True):","sourceCodeStart":170,"sourceCodeEnd":206,"githubUrl":"https://github.com/microsoft/autogen/blob/027ecf0a379bcc1d09956d46d12d44a3ad9cee14/python/packages/autogen-agentchat/src/autogen_agentchat/teams/_group_chat/_selector_group_chat.py#L170-L206","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Guarantee a non-empty fallback in the candidate function: return filtered or self._participant_names (i.e. fall back to all participants).","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).","Log the thread state inside candidate_func when it returns empty to find which condition over-filters."],"exampleFix":"# before\ndef candidates(thread):\n    return [p for p in names if p != thread[-1].source]  # empty when only 1 participant\n\n# after\ndef candidates(thread):\n    filtered = [p for p in names if p != thread[-1].source]\n    return filtered if filtered else list(names)  # never empty","handlingStrategy":"validation","validationCode":"def candidates(thread):\n    result = [p for p in participant_names if condition(thread, p)]\n    return result if result else list(participant_names)  # never empty","typeGuard":"def is_nonempty_name_list(value: object) -> bool:\n    return isinstance(value, list) and len(value) > 0 and all(isinstance(x, str) for x in value)","tryCatchPattern":null,"preventionTips":["Always provide a fallback candidate set in candidate functions.","Unit-test candidate functions against the first-turn (empty) thread."],"tags":["autogen","selector","candidate-func","validation"],"backgroundTag":null,"analyzedSha":"027ecf0a379bcc1d09956d46d12d44a3ad9cee14","analyzedAt":"2026-08-15T03:38:00.719Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}