run-llama/llama_index · error · ValueError
Unexpected type: {type(choice)}
Error message
Unexpected type: {type(choice)} What it means
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.
Source
Thrown at llama-index-core/llama_index/core/base/base_selector.py:60
def inds(self) -> List[int]:
return [x.index for x in self.selections]
@property
def reasons(self) -> List[str]:
return [x.reason for x in self.selections]
# separate name for clarity and to not confuse function calling model
SelectorResult = MultiSelection
def _wrap_choice(choice: MetadataType) -> ToolMetadata:
if isinstance(choice, ToolMetadata):
return choice
elif isinstance(choice, str):
return ToolMetadata(description=choice)
else:
raise ValueError(f"Unexpected type: {type(choice)}")
def _wrap_query(query: QueryType) -> QueryBundle:
if isinstance(query, QueryBundle):
return query
elif isinstance(query, str):
return QueryBundle(query_str=query)
else:
raise ValueError(f"Unexpected type: {type(query)}")
class BaseSelector(PromptMixin, DispatcherSpanMixin):
"""Base selector."""
def _get_prompt_modules(self) -> PromptMixinType:
"""Get prompt sub-modules."""
return {}
View on GitHub (pinned to afd0fef371)
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].
Example fix
# before
result = selector.select(choices=[query_engine_tool, "other"], query=q)
# after
from llama_index.core.llms import ToolMetadata
result = selector.select(
choices=[query_engine_tool.metadata, "other"], query=q
) Defensive patterns
Strategy: type-guard
Validate before calling
from llama_index.core.tools import ToolMetadata choices = [c.metadata if hasattr(c, "metadata") else c for c in raw_choices] assert all(isinstance(c, (str, ToolMetadata)) for c in choices)
Type guard
def is_valid_choice(c: Any) -> bool:
return isinstance(c, (str, ToolMetadata)) Prevention
- Pass tool.metadata for tool objects, never the tool itself.
- Validate the choices list once at the boundary of your routing code.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- Unexpected type: {type(query)}
- There are {len(self.reasons)} selections, please use .reason
- embeddings_cache must be of type BaseKVStore
- Invalid message content: {message.content!s}
- image_node.image is neither a string or None.
AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15).
Data as JSON: /api/errors/46658b90e542283d.
Report an issue: GitHub.