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

  1. Pass each choice as a str (a description) or an explicit ToolMetadata object.
  2. If you have tool objects like FunctionTool/QueryEngineTool, pass tool.metadata instead of the tool itself.
  3. 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

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


AI-assisted analysis of run-llama/llama_index@afd0fef371 (2026-08-15). Data as JSON: /api/errors/46658b90e542283d. Report an issue: GitHub.