run-llama/llama_index · error · ValueError

There are {len(self.reasons)} selections, please use .reason

Error message

There are {len(self.reasons)} selections, please use .reasons.

What it means

Raised by SelectorResult.reason when a multi-selection selector result holds more than (or fewer than) exactly one selection. The single-value accessor .reason only works when len(self.selections) == 1; for multi-choice routing you must read the list property .reasons instead. Note the message says '.reasons' while the sibling property .ind tells you to use .inds' plural accessors.

Source

Thrown at llama-index-core/llama_index/core/base/base_selector.py:36


class MultiSelection(BaseModel):
    """A multi-selection of choices."""

    selections: List[SingleSelection]

    @property
    def ind(self) -> int:
        if len(self.selections) != 1:
            raise ValueError(
                f"There are {len(self.selections)} selections, please use .inds."
            )
        return self.selections[0].index

    @property
    def reason(self) -> str:
        if len(self.reasons) != 1:
            raise ValueError(
                f"There are {len(self.reasons)} selections, please use .reasons."
            )
        return self.selections[0].reason

    @property
    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:

View on GitHub (pinned to afd0fef371)

Solutions

  1. If you expect multiple selections, read selector_result.reasons (list of str) and selector_result.inds (list of int) instead of .reason/.ind.
  2. If you expect exactly one selection, switch the selector to a single selector (e.g. PydanticSingleSelector or LLMSingleSelector) so only one selection is produced.
  3. Inspect len(selector_result.selections) before choosing the accessor and branch accordingly.

Example fix

// before
sel = await selector.select(choices, query)
reason = sel.reason  # ValueError if multiple selections

// after
sel = await selector.select(choices, query)
if len(sel.selections) == 1:
    reason = sel.reason
else:
    reasons = sel.reasons  # List[str]
Defensive patterns

Strategy: type-guard

Validate before calling

result = selector.select(choices, query)
if len(result.selections) != 1:
    reasons = result.reasons
    inds = result.inds
else:
    reason, ind = result.reason, result.ind

Type guard

def is_single_selection(r: SelectorResult) -> bool:
    return len(r.selections) == 1

Try / catch

try:
    reason = result.reason
except ValueError:
    reason = result.reasons  # fall back to list accessor

Prevention

When it happens

Trigger: Calling selector_result.reason after a selector (e.g. PydanticSingleSelector vs PydanticMultiSelector) ran with multiple choices, or when the LLM returned multiple JSON selections. Any .select() call over >1 choices using a multi-selector returns a MultiSelection where len(selections) != 1.

Common situations: Switching a router/query engine from a single-selector to a multi-selector (e.g. RouterQueryEngine with selector_type=PydanticMultiSelector) but still reading .reason/.ind; LLM emitting several selections for a single-choice prompt.

Related errors


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