deepset-ai/haystack · error · NoRouteSelectedException
No route fired. Routes: {self.routes}
Error message
No route fired. Routes: {self.routes} What it means
run() evaluated every route's condition and none rendered truthy, so no output could be selected. Haystack raises NoRouteSelectedException listing all configured routes to help debug why each condition was False.
Source
Thrown at haystack/components/routers/conditional_router.py:480
if not self._unsafe:
output_value = ast.literal_eval(output_value)
# Validate output type if needed
if self._validate_output_type and not self._output_matches_type(output_value, output_type):
raise ValueError(f"Route '{output_name}' type doesn't match expected type") # noqa: TRY301
result[output_name] = output_value
return result
except Exception as e:
# If this was a type-validation failure or missing passthrough variable, let it propagate
if isinstance(e, ValueError):
raise
msg = f"Error evaluating condition for route '{route}': {e}"
raise RouteConditionException(msg) from e
raise NoRouteSelectedException(f"No route fired. Routes: {self.routes}")
def _validate_routes(self, routes: list[Route]) -> None:
"""
Validates a list of routes.
:param routes: A list of routes.
"""
for route in routes:
try:
keys = set(route.keys())
except AttributeError as e:
raise ValueError(f"Route must be a dictionary, got: {route}") from e
mandatory_fields = {"condition", "output", "output_type", "output_name"}
has_all_mandatory_fields = mandatory_fields.issubset(keys)
if not has_all_mandatory_fields:
raise ValueError(
f"Route must contain 'condition', 'output', 'output_type' and 'output_name' fields: {route}"View on GitHub (pinned to e318778c9b)
Solutions
- Add a final catch-all route with condition "True" as a default.
- Print/log the inputs passed to run() and test each condition template against them.
- Ensure conditions render to a Python-literal boolean in safe mode (e.g. "{{ query|length > 10 }}").
- Catch NoRouteSelectedException and implement fallback logic at the call site.
Example fix
// before
routes=[Route(condition="q == 'a'", output="a", output_type=str, output_name="out")]
// after
routes=[Route(condition="q == 'a'", output="a", output_type=str, output_name="out"),
Route(condition="True", output="default", output_type=str, output_name="default")] Defensive patterns
Strategy: try-catch
Validate before calling
fired = any(eval_literal(env.from_string(r["condition"]).render(**kwargs))
for r in router.routes)
assert fired, "no route condition is True for given inputs" Type guard
def some_route_fires(router, kwargs: dict) -> bool:
import ast
for r in router.routes:
try:
if ast.literal_eval(router._env.from_string(r["condition"]).render(**kwargs)):
return True
except Exception:
continue
return False Try / catch
try:
result = router.run(**kwargs)
except NoRouteSelectedException:
result = {"default": fallback_value} Prevention
- Always include a catch-all route with condition "True" as the last entry.
- Log router inputs when debugging which conditions evaluate false.
- Cover all upstream output variants in your route conditions.
When it happens
Trigger: ConditionalRouter.run() where every route's condition renders to False (or fails ast.literal_eval into a falsy value in safe mode) for the given inputs.
Common situations: Conditions that don't cover all input cases (no catch-all else route); variables rendered as strings ("True"/"False" handled, but comparisons like "1 == 2" fine while free text literal_eval fails to False-ish values); upstream component producing unexpected values.
Related errors
- Route '{output_name}' type doesn't match expected type
- Error evaluating condition for route '{route}': {e}
- Invalid template for condition: {condition_value!r} (type: {
- Invalid Jinja template '{template}': {e}
- No input data provided for output adaptation
AI-assisted analysis of deepset-ai/haystack@e318778c9b (2026-08-30).
Data as JSON: /api/errors/9e02c200420109e4.
Report an issue: GitHub.