{"record":{"id":"46658b90e542283d","repo":"run-llama/llama_index","slug":"unexpected-type-type-choice","errorCode":null,"errorMessage":"Unexpected type: {type(choice)}","messagePattern":"Unexpected type: (.+?)","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"llama-index-core/llama_index/core/base/base_selector.py","lineNumber":60,"sourceCode":"    def inds(self) -> List[int]:\n        return [x.index for x in self.selections]\n\n    @property\n    def reasons(self) -> List[str]:\n        return [x.reason for x in self.selections]\n\n\n# separate name for clarity and to not confuse function calling model\nSelectorResult = MultiSelection\n\n\ndef _wrap_choice(choice: MetadataType) -> ToolMetadata:\n    if isinstance(choice, ToolMetadata):\n        return choice\n    elif isinstance(choice, str):\n        return ToolMetadata(description=choice)\n    else:\n        raise ValueError(f\"Unexpected type: {type(choice)}\")\n\n\ndef _wrap_query(query: QueryType) -> QueryBundle:\n    if isinstance(query, QueryBundle):\n        return query\n    elif isinstance(query, str):\n        return QueryBundle(query_str=query)\n    else:\n        raise ValueError(f\"Unexpected type: {type(query)}\")\n\n\nclass BaseSelector(PromptMixin, DispatcherSpanMixin):\n    \"\"\"Base selector.\"\"\"\n\n    def _get_prompt_modules(self) -> PromptMixinType:\n        \"\"\"Get prompt sub-modules.\"\"\"\n        return {}\n","sourceCodeStart":42,"sourceCodeEnd":78,"githubUrl":"https://github.com/run-llama/llama_index/blob/afd0fef371831f9bda13e5af7167cf4e981278ab/llama-index-core/llama_index/core/base/base_selector.py#L42-L78","documentation":"Raised by _wrap_choice when an entry in the choices sequence passed to BaseSelector.select is neither a ToolMetadata instance nor a plain str. The helper normalizes selector choices by wrapping strings into ToolMetadata(description=choice); any other type is rejected.","triggerScenarios":"Calling selector.select(choices=[...]) with a list containing objects other than str or ToolMetadata, e.g. a dict, a QueryEngine, a FunctionTool, or a custom metadata class.","commonSituations":"Passing raw dicts or tool objects (e.g. QueryEngineTool or FunctionTool instances) directly to a Selector instead of their .metadata attribute; migrating code that assumed dicts were accepted.","solutions":["Pass each choice as a str (a description) or an explicit ToolMetadata object.","If you have tool objects like FunctionTool/QueryEngineTool, pass tool.metadata instead of the tool itself.","Sanitize the choices list before calling select: [c if isinstance(c, (str, ToolMetadata)) else c.metadata for c in choices]."],"exampleFix":"# before\nresult = selector.select(choices=[query_engine_tool, \"other\"], query=q)\n\n# after\nfrom llama_index.core.llms import ToolMetadata\nresult = selector.select(\n    choices=[query_engine_tool.metadata, \"other\"], query=q\n)","handlingStrategy":"type-guard","validationCode":"from llama_index.core.tools import ToolMetadata\nchoices = [c.metadata if hasattr(c, \"metadata\") else c for c in raw_choices]\nassert all(isinstance(c, (str, ToolMetadata)) for c in choices)","typeGuard":"def is_valid_choice(c: Any) -> bool:\n    return isinstance(c, (str, ToolMetadata))","tryCatchPattern":null,"preventionTips":["Pass tool.metadata for tool objects, never the tool itself.","Validate the choices list once at the boundary of your routing code."],"tags":["llama-index","selector","type-validation","tool-metadata"],"backgroundTag":null,"analyzedSha":"afd0fef371831f9bda13e5af7167cf4e981278ab","analyzedAt":"2026-08-15T05:42:58.429Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}